Setup

library(tidyverse)
library(meta)
library(cowplot)
library(here)

source(here("helper_functions/data_import_functions.R"))
source(here("helper_functions/figure_format_functions.R"))
source(here("helper_functions/calculation_functions.R"))
all_hla_expanded <- readRDS(here("2_accuracy/all_hla_expanded.RDS"))
isb_path <- here("data/isb")

Running scHLA count

  • NOTE: Filepaths for running scHLAcount on raw sequencing data have not been refactored for github repo. All output files relevant for downstream analysis have been moved to data/isb/scHLAcount within the github repo.

Prepare reference genotype files

# Assemble key linking sample-pool to simplified sample
hla_samples <- read_tsv(here("data/isb/scHLAcount/BL_fastq_files.txt"), col_names = "file") %>% 
  filter(grepl("^INCOV", file)) %>% 
  filter(grepl("-BL", file)) %>% 
  separate(file, into = c("sample", NA), sep = "_", remove = F) %>% 
  drop_na()

# From all genotype field results, assemble highest resolution genotype for all sample:genotypers
select_last_allele <- function(x){
  x[!is.na(x)] %>%
    tail(1) %>% 
    str_replace_all("_", ":")}
hla_key <- all_hla_expanded %>% 
  rowwise() %>% 
  filter(locus %in% c("A","B","C","DPA1","DPB1","DQA1","DQB1","DRB1")) %>% 
  mutate(allele = select_last_allele(across(contains("field")))) %>% 
  select(sample, genotyper, locus, allele) %>% 
  unite(allele, locus, allele, sep = "*")

# Merge into key of list of genotypes for each sample-pool:genotyper pair
hla_merge <- hla_samples %>% 
  left_join(hla_key, by = "sample") %>% 
  drop_na() %>% 
  select(-sample) %>% 
  group_by(file, genotyper) %>% 
  nest()

# # Write keys to set of csvs for input to scHLAcount
# hla_merge %>%
#   mutate(write = pmap(list(data, file, genotyper), function(d,f,g){
#     dir <- here(sprintf("data/isb/scHLAcount/genotypes/%s",g))
#     if (!dir.exists(dir)){dir.create(dir, recursive = T)}
#     write_tsv(d,
#               sprintf("%s/%s_hla.tsv",dir,f),
#               col_names = F,
#               )}))

Run script

sbatch /covid/scripts/isb_scHLAcount_benchmark.sh

#!/bin/sh

# SET GLOBAL VARIABLES
# General
export GIT_DIR=/labs/khatrilab/solomonb/hla_project/hla_benchmark/data/isb/scHLAcount
export BASE_DIR=/labs/khatrilab/solomonb/covid/isb/scHLAcount
export LOG_DIR=$GIT_DIR/logs/$(date +'%y%m%d_%H%M%S')
# FASTQ/HISAT
export INDEX_DIR=/labs/khatrilab/solomonb/rnaseq_processing/hisat2/hisat_arcas/hisat_data/grch38
export BAM_DIR=$BASE_DIR/bam
# HLA references
export HLA_DIR=$GIT_DIR/hla_references
export HLANUC=/labs/khatrilab/solomonb/references/IMGTHLA/hla_nuc.fasta
export HLAGEN=/labs/khatrilab/solomonb/references/IMGTHLA/hla_gen.fasta
# SCHLA
export BARCODE_DIR=$GIT_DIR/barcodes
export GENOTYPE_DIR=$GIT_DIR/genotypes
export SCHLACOUNT_DIR=$GIT_DIR/output
export TEMP_DIR=$GIT_DIR/temp_fastq
# SLURM 
export N_CORES=$SLURM_CPUS_PER_TASK


# CREATE DIRECTORIES
if [ ! -d $LOG_DIR ]; then mkdir -p $LOG_DIR;fi
if [ ! -d $BAM_DIR ]; then mkdir -p $BAM_DIR;fi
if [ ! -d $SCHLACOUNT_DIR ]; then mkdir -p $SCHLACOUNT_DIR;fi
if [ ! -d $TEMP_DIR ]; then mkdir -p $TEMP_DIR;fi

# CREATE HLA REFERECE ##########################################################
HLA_REFERENCE(){
  source /labs/khatrilab/solomonb/miniconda3/etc/profile.d/conda.sh
  conda activate samtools
  
  printf "\n\
########################## CREATE REFERENCE ####################################\
  \n" >> $LOG_DIR/${1}/${1}_${2}.log
  printf "\n### [START___REFERENCE___$(date +'%D %X')]\n" >> $LOG_DIR/${1}/${1}_${2}.log
  
  [ ! -d $HLA_DIR/${2} ] && mkdir -p $HLA_DIR/${2}
  
  while read -r line; do grep -F -m 1 $line $HLANUC >> $HLA_DIR/${2}/${1}_tmpallele.txt; done < $GENOTYPE_DIR/${2}/${1}_hla.tsv
  
  samtools faidx  $HLANUC $(cut -f1 -d' ' $HLA_DIR/${2}/${1}_tmpallele.txt | tr '>' ' ' | tr '\n' ' ') > $HLA_DIR/${2}/${1}_cds.fasta 2>> $LOG_DIR/${1}/${1}_${2}.log
  samtools faidx  $HLAGEN $(cut -f1 -d' ' $HLA_DIR/${2}/${1}_tmpallele.txt | tr '>' ' ' | tr '\n' ' ') > $HLA_DIR/${2}/${1}_gen.fasta 2>> $LOG_DIR/${1}/${1}_${2}.log
  
  while read -r line; do IFS=' '; read -r f1 f2 <<<"$line"; sed -i"" "s/$f1/$f1 $f2/g" $HLA_DIR/${2}/${1}_cds.fasta; done < $HLA_DIR/${2}/${1}_tmpallele.txt 2>> $LOG_DIR/${1}/${1}_${2}.log
  while read -r line; do IFS=' '; read -r f1 f2 f3 <<<"$line"; sed -i"" "s/$f1/$f1 $f2/g" $HLA_DIR/${2}/${1}_gen.fasta; done < $HLA_DIR/${2}/${1}_tmpallele.txt 2>> $LOG_DIR/${1}/${1}_${2}.log
  
  rm $HLA_DIR/${2}/${1}_tmpallele.txt
  printf "### [COMPLETE___REFERENCE___$(date +'%D %X')]\n" >> $LOG_DIR/${1}/${1}_${2}.log
}
export -f HLA_REFERENCE


# DEFINE scHLA GENOTYPING PIPELINE #############################################
SCHLACOUNT(){
  source /labs/khatrilab/solomonb/miniconda3/etc/profile.d/conda.sh
  conda activate samtools
  
  printf "\n\
########################## RUN SCHLACOUNT ####################################\
  \n" >> $LOG_DIR/${1}/${1}_${2}.log
  printf "\n### [START___SCHLACOUNT___$(date +'%D %X')]\n" >> $LOG_DIR/${1}/${1}_${2}.log
  
  echo "### Starting  scHLAcount at $(date +'%D %X')" >> $LOG_DIR/${1}/${1}_${2}.log
  
  # if [ -d $SCHLACOUNT_DIR/${1}_results ]; then rm -r $SCHLACOUNT_DIR/${1}_results;fi
  [ ! -d SCHLACOUNT_DIR/${2} ] && mkdir -p $SCHLACOUNT_DIR/${2}

  sc_hla_count \
  --bam $BAM_DIR/${1}.bam \
  --cell-barcodes $BARCODE_DIR/${1}_barcode.tsv \
  --out-dir $SCHLACOUNT_DIR/${2}/${1}_results \
  --fasta-cds $HLA_DIR/${2}/${1}_cds.fasta \
  --fasta-genomic $HLA_DIR/${2}/${1}_gen.fasta\
  >> $LOG_DIR/${1}/${1}_${2}.log \
  2>> $LOG_DIR/${1}/${1}_${2}.log

  printf "### [COMPLETE___SCHLACOUNT___$(date +'%D %X')]\n" >> $LOG_DIR/${1}/${1}_${2}.log
}
export -f SCHLACOUNT


# DEFINE PIPELINE CONTROLLER FUNCTION ##########################################
PIPELINE(){
  echo "START: sample $1 at $(date +'%D %X')"
  for G in arcasHLA hlaminer invitro optitype phlat
  do
    [ ! -d $LOG_DIR/${1} ] && mkdir -p $LOG_DIR/${1}
    HLA_REFERENCE $1 $G
    SCHLACOUNT $1 $G
  done
  echo "COMPLETE: sample $1 at $(date +'%D %X')"
}
export -f PIPELINE

cat $GIT_DIR/BL_fastq_files.txt | parallel --delay 15 -j $SLURM_NTASKS --joblog $LOG_DIR/parallel.log PIPELINE {}

UMAP projections

Cell clusters

# srt <- readRDS("/labs/khatrilab/hongzheng/webapps/isb/srt_isb260.meta.rds")
srt <- readRDS(here("data/isb/isb_sc_metadata.RDS"))
cells <- c("CD14 Monocyte", "CD4 T", "CD8 T", "NK", "B", "CD16 Monocyte", "cDC", "pDC")
srt <- srt %>% select(sample, sampleID, cell, celltype, severity, UMAP_1, UMAP_2) %>% 
  filter(celltype %in% cells)
# ggplot theme to overlay cluster id #s on UMAP
gg_srt_relabel <- function(df, x_var, y_var, color_var, cell_fraction = 1){
  plt <- df %>% 
    ggplot(aes(x = !!sym(x_var), y = !!sym(y_var), color = !!sym(color_var))) +
    geom_point(size = 0.5, alpha = 0.5)+
    theme_bw() +
    scale_color_brewer(palette = "Dark2")+
    guides(color = guide_legend(override.aes = list(size = 2, alpha = 1)))
  
  g <- ggplot_build(plt)
  
  plt_ids <- g$data[[1]]
  group_levels <- levels(factor(g$plot$data[[g$plot$labels$colour]]))
  
  plt_key <- g$data[[1]] %>% 
    select(colour, group) %>% 
    distinct() %>% 
    mutate(label = map_chr(group, function(x) group_levels[x])) %>% 
    mutate(label = factor(label, level = group_levels)) %>%
    mutate(label = sprintf("%s) %s", 1:n(), label)) %>% 
    select(-group)
  
  plt_df <- plt_ids %>% 
    left_join(plt_key, by = "colour")
  
  plt_center <- plt_df %>% 
    group_by(label) %>% summarise(x = mean(x), y = mean(y)) %>%
    mutate(label = gsub(").*","",label))
  
  plt_repel <- plt_df %>% 
    ggplot(aes(x=x,y=y,color=label)) +
    geom_point(size = 0.5)+
    ggrepel::geom_text_repel(data=plt_center,
                             aes(label=label,  bg.color="white", bg.r=0.25),
                             color = "black",
                             fontface = "bold") +
    theme_bw() +
    guides(color = guide_legend(override.aes = list(size = 2) ) ) +
    labs(x=x_var, y = y_var, color = color_var) +
    theme(axis.text = element_blank(), axis.ticks = element_blank())
  return(plt_repel)
}

plt_srt <- srt %>% 
  filter(celltype %in% cells) %>% 
  filter(sampleID == "INCOV069-BL") %>% 
  gg_srt_relabel(x_var = "UMAP_1", y_var = "UMAP_2", color_var = "celltype", cell_fraction = 0.1) +
  scale_color_brewer(palette = "Dark2")+ 
  theme(axis.text = element_blank(), axis.ticks = element_blank()) +
  labs(x="UMAP 1", y = "UMAP 2", color = "Cell Cluster")
plt_srt

Allele frequency

# samp <- "INCOV069-BL"
# samp <- "INCOV022-BL"
# samp <- "INCOV083-BL"
samp <- "INCOV028-BL"
scHLAcount_dir <- sprintf("%s/scHLAcount", isb_path)

# Create tibble of all genotyper and sample combinations
allele_data <- expand_grid(
  genotyper = c("invitro", "arcasHLA", "optitype", "phlat", "hlaminer"),
  sample = read_lines(here("data/isb/scHLAcount/BL_fastq_files.txt"))) %>% 
  filter(grepl(samp, sample)) %>% 
  # Import data based on sample and genotyper
  mutate(result_path = sprintf("%s/scHLAcount/output/%s",isb_path, genotyper),
         barcode_path = sprintf("%s/scHLAcount/barcodes", isb_path)) %>% 
  mutate(data = pmap(list(sample, result_path, barcode_path), function(s,r,b){
    df <- scHLA_data_processing(
      sample=s,
      result_dir=r,
      barcode_dir=b
    ) 
  })) %>% unnest(data)
plot_allele_frequencies <- function(allele_data, gene_select){

  allele_data_ratios <- allele_data %>% 
    filter(gene == gene_select) %>% 
    mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
    mutate(allele_order = fct_recode(factor(allele_order), "Allele 1" = "1", "Allele 2" = "2"))
    
  allele_label <- allele_data_ratios %>% 
    select(genotyper, allele_order, allele) %>% 
    distinct()
  
  plt <- srt %>% 
    left_join(allele_data_ratios %>% filter(gene == gene_select), by = "cell") %>% 
    filter(!is.na(allele)) %>% 
    ggplot(aes(x = UMAP_1, y = UMAP_2, color = allele_ratio)) +
    geom_point(size = 0.5, alpha = 0.5)+
    theme_bw() +
    facet_grid(allele_order~genotyper)+
    scale_color_gradient2(high = "red", mid = "grey80", low = "blue", midpoint = 0.5, na.value = "transparent") +
    labs(x="UMAP 1", y="UMAP 2", color = "Allele \nFrequency")+
    theme(axis.ticks = element_blank(), axis.text = element_blank())+
    coord_cartesian(ylim = c(-10,15))
  
  plt_allele_freq <- plt+
    geom_label(data = allele_label, aes(x=-10,y=14, color = NULL, label = allele), size = 3, hjust = 0)
  
  return(plt_allele_freq)
}

plt_allele_freq <- plot_allele_frequencies(allele_data = allele_data, gene_select = "A")
plt_allele_freq

plot_allele_frequencies(allele_data = allele_data, gene_select = "DRB1")

Maximum allele

plot_allele_ratios <- function(allele_data, gene_select){
  # Create tibble of all genotyper and sample combinations
  allele_max_ratio <- allele_data  %>%
    select(genotyper, cell, gene, allele_order, allele_ratio) %>%
    # group_by(genotyper, cell, allele_order) %>%  mutate(test = length(allele_order))
    pivot_wider(names_from = "allele_order", values_from = "allele_ratio", names_prefix = "allele_") %>%
    rowwise() %>%
    mutate(max_ratio = max(across(contains("allele_")), na.rm = T)) %>%
    select(genotyper, cell, gene, max_ratio) %>%
    filter(gene == gene_select) 
  
  allele_label <- allele_data %>% select(sample, genotyper, gene, allele) %>% 
    filter(gene == gene_select) %>% 
    separate(sample, into = c("sample", NULL), sep = "_", extra = "drop") %>% 
    distinct() %>% 
    group_by(sample, genotyper, gene) %>% nest() %>% unnest_wider(data) %>% 
    mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
    mutate(allele = map_chr(allele, paste, collapse = "\n"))
  
  plt_max_allele <- srt %>% 
    left_join(allele_max_ratio %>% filter(gene == gene_select), by = "cell") %>% 
    # mutate(genotyper = fct_relevel(genotyper, "invitro")) %>% 
    mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
    filter(!is.na(genotyper)) %>%
    ggplot(aes(x = UMAP_1, y = UMAP_2, color = max_ratio)) +
      geom_point(size = 0.5, alpha = 0.5)+
      geom_label(data = allele_label, aes(color = NULL, label = allele, x = -Inf, y = -Inf),
              hjust = -0.1, vjust = -0.1, size = 3, hjust = 0) +
      theme_bw() +
      facet_grid(.~genotyper)+
      scale_color_gradient(high = "red", low = "blue", na.value = "transparent") +
      labs(x="UMAP 1", y="UMAP 2", color = "Maximum Allele \nFrequency")+
      theme(axis.ticks = element_blank(), axis.text = element_blank())
  
  return(plt_max_allele)
}

plt_max_allele <- plot_allele_ratios(allele_data = allele_data, gene_select = "A")
plt_max_allele

plot_allele_ratios(allele_data = allele_data, gene_select = "DRB1")

allele_max_ratio <- allele_data  %>%
    filter(genotyper == "invitro") %>%  
    select(cell, gene, allele_order, allele_ratio) %>%
    pivot_wider(names_from = "allele_order", values_from = "allele_ratio", names_prefix = "allele_") %>%
    rowwise() %>%
    mutate(max_ratio = max(across(contains("allele_")), na.rm = T)) %>%
    select(cell, gene, max_ratio)
  
allele_label <- allele_data %>%  
  filter(genotyper == "invitro") %>% 
  select(sample, gene, allele) %>%
  separate(sample, into = c("sample", NULL), sep = "_", extra = "drop") %>% 
  distinct() %>% 
  group_by(sample, gene) %>% nest() %>% unnest_wider(data) %>% 
  mutate(allele = map_chr(allele, paste, collapse = "\n"))

srt %>% 
  left_join(allele_max_ratio, by = "cell") %>% 
  # mutate(genotyper = fct_relevel(genotyper, "invitro")) %>% 
  filter(!is.na(max_ratio)) %>%
  ggplot(aes(x = UMAP_1, y = UMAP_2, color = max_ratio)) +
    geom_point(size = 0.5, alpha = 0.5)+
    geom_label(data = allele_label, aes(color = NULL, label = allele, x = -Inf, y = -Inf),
            hjust = -0.1, vjust = -0.1, size = 3, hjust = 0) +
    theme_bw() +
    facet_grid(.~gene)+
    scale_color_gradient(high = "red", low = "blue", na.value = "transparent") +
    labs(x="UMAP 1", y="UMAP 2", color = "Maximum Allele \nFrequency")+
    theme(axis.ticks = element_blank(), axis.text = element_blank())

  allele_max_ratio <- allele_data  %>%
    select(genotyper, cell, gene, allele_order, allele_ratio) %>%
    # group_by(genotyper, cell, allele_order) %>%  mutate(test = length(allele_order))
    pivot_wider(names_from = "allele_order", values_from = "allele_ratio", names_prefix = "allele_") %>%
    rowwise() %>%
    mutate(max_ratio = max(across(contains("allele_")), na.rm = T)) %>%
    select(genotyper, cell, gene, max_ratio)
  
  allele_label <- allele_data %>% select(sample, genotyper, gene, allele) %>% 
    separate(sample, into = c("sample", NULL), sep = "_", extra = "drop") %>% 
    distinct() %>% 
    group_by(sample, genotyper, gene) %>% nest() %>% unnest_wider(data) %>% 
    mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
    mutate(allele = map_chr(allele, paste, collapse = "\n"))
  
  plt_max_allele_allGenesTypers <- srt %>% 
    left_join(allele_max_ratio, by = c("cell")) %>% 
    # mutate(genotyper = fct_relevel(genotyper, "invitro")) %>% 
    mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
    filter(!is.na(max_ratio)) %>%
    ggplot(aes(x = UMAP_1, y = UMAP_2, color = max_ratio)) +
      rasterise(geom_point(size = 0.5, alpha = 0.5), dpi = 300)+
      geom_label(data = allele_label, aes(color = NULL, label = allele, x = -Inf, y = -Inf),
              hjust = -0.1, vjust = -0.1, size = 3, hjust = 0) +
      theme_bw() +
      facet_grid(genotyper~gene)+
      scale_color_gradient(high = "red", low = "blue", na.value = "transparent") +
      labs(x="UMAP 1", y="UMAP 2", color = "Maximum Allele \nFrequency")+
      theme(axis.ticks = element_blank(), axis.text = element_blank())
  
  plt_max_allele_allGenesTypers

Meta-analysis approach

Rationale

  • Goal: determine a summary statistic for allele ratio across all cells
  • Problem:
    • Each cell in scRNA experiment has its own ratio of HLA allele 1 reads : HLA allele 2 reads
    • A wide range of total read counts may go into each cell’s ratio
    • Small difference in read counts for cells with low total reads can greatly alter the allele ratio compared to cells with high total read counts
      • I.e. Cells with low read counts are more likely to have imprecise allele ratios
    • Simply averaging ratios would equally weight high confidence, high read count ratios with low confidence, low read ratios
  • Approach
    • Meta-analysis methods specifically address type of problem, typically by weighing an effect by the inverse of its variance
    • In the setting of ratios, basic approach would be to combine log-odds ratio of alleles, weighted by inverse variance
    • Would use a random effects model because do not expect each cell in a group would have the same exact ratio due to various stochastic transcriptional effects

Calculating summary effect sizes

  • Analysis can take some time, so run on an HPC cluster using slurm
  • Contained in a separate script sc_meta.R:

Contents of sc_meta.R

library(tidyverse)
library(meta)

source(here("helper_functions/data_import_functions.R"))
isb_path <- here("data/isb")

srt <- readRDS(here("data/isb/isb_sc_metadata.RDS"))
cells <- c("CD14 Monocyte", "CD4 T", "CD8 T", "NK", "B", "CD16 Monocyte", "cDC", "pDC")
srt <- srt %>% select(sample, sampleID, cell, celltype, severity, UMAP_1, UMAP_2) %>% 
  filter(celltype %in% cells)

# Function to perform meta-analysis on dataframe where
# each row is a cell and columns:
# `observed` (reads of dominant allele)
# `gene_sum_typed` (total cell reads)
# `expected` (reads of one allele expected if 50:50 allele_1 : allele_2)
sc_meta <- function(df){
  l <- nrow(df)
  m <- metabin(event.e = observed, n.e = gene_sum_typed, event.c = expected, n.c = gene_sum_typed,
               data = df,
               method = "Inverse",
               incr = 0.1,
               sm = "OR")
  data.frame(summary(m)$random) %>% 
    mutate(n_cells = l) %>% 
    select(TE, seTE, lower, upper, n_cells) 
}

# Import data based on sample and genotyper
cell_stats <- expand_grid(
  genotyper = c("invitro", "arcasHLA", "optitype", "phlat", "hlaminer"),
  sample = read_lines(here("data/isb/scHLAcount/BL_fastq_files.txt"))) %>% 
  # head(5) %>% # Specify limit to number of meta-analyses
  mutate(data = map2(sample, genotyper, function(s,g){
    result_path = sprintf("%s/scHLAcount/output_ase/%s",isb_path, g)
    barcode_path = sprintf("%s/scHLAcount/barcodes", isb_path)
    scHLA_data_processing(sample = s,
                          result_dir = result_path,
                          barcode_dir = barcode_path)
  })) %>% unnest(data) %>% 
  filter(!is.na(cell)) %>% 
  mutate(sample = gsub("_[A-Z][0-9]$","",sample)) %>% # Consolidate samples
  left_join(srt %>% select(celltype, cell), by = "cell") %>% # Add celltypes
  filter(celltype %in% cells) # Keep only standard cell types

meta_df <- cell_stats %>% 
  # Keep only most expressed allele (or random if 50:50)
  group_by(sample, genotyper, gene, cell) %>% 
  slice_max(order_by = allele_ratio, n = 1, with_ties = F) %>% 
  ungroup() %>% 
  # Fill out contingency table
  mutate(complement = gene_sum_typed - count, 
         expected = 0.5*gene_sum_typed) %>% 
  rename(observed = count) %>% 
  select(sample, genotyper, celltype, gene, observed, expected, gene_sum_typed) %>% 
  group_by(sample, genotyper, celltype, gene) %>%
  # Nest and run meta-analysis
  nest() %>%
  ungroup() %>% 
  mutate(data = map(data,function(x) {sc_meta(x)})) %>%
  unnest(data)

write_csv(meta_df, here("7_HLA_ASE/meta_analysis_results_12.csv"))

Single sample analysis

meta_df <- read_csv(here("7_HLA_ASE/meta_analysis_results_c12.csv")) 
Rows: 16440 Columns: 9
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): sample, genotyper, celltype, gene
dbl (5): TE, seTE, lower, upper, n_cells

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
meta_df
plot_meta_odds <- function(df, cell_type, locus){
  df %>% 
  filter(celltype == cell_type, gene == locus) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>%
  ggplot(aes(x = genotyper, y = TE, ymin = lower, ymax = upper))+
  geom_point()+
  geom_errorbar(width = 0.4)+
  theme_bw()+
  scale_y_continuous(limits = c(0,NA))+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio of dominant allele", x= NULL)
}

plt_meta <- meta_df %>% 
  filter(sample == samp) %>% 
  plot_meta_odds(cell_type = "cDC", locus = "A")

plt_meta

meta_df %>% 
  filter(sample == samp) %>%
  plot_meta_odds(cell_type = "cDC", locus = "DRB1")

samples <- unique(meta_df$sample)
for (i in 1:length(samples)){
plt_meta <- meta_df %>% 
  filter(sample == samples[i]) %>% 
  plot_meta_odds(cell_type = "CD14 Monocyte", locus = "A") +
  ggtitle(samples[i])
  print(plt_meta)
}

plt_meta_allLoci <- meta_df %>% 
  filter(sample == samp) %>%
  filter(celltype == "cDC") %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>%
  ggplot(aes(x = genotyper, y = TE, ymin = lower, ymax = upper))+
  geom_point()+
  geom_errorbar(width = 0.4)+
  facet_grid(.~gene)+
  theme_bw()+
  scale_y_continuous(limits = c(0,NA))+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio\nof dominant allele", x= NULL)

plt_meta_allLoci  

Multi-sample analysis

meta_df <- read_csv(here("7_HLA_ASE/meta_analysis_results_c12.csv")) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper),
         celltype = factor(celltype, levels = c(
          "cDC", "CD14 Monocyte", "B", "pDC", "CD16 Monocyte", "CD8 T", "CD4 T", "NK")))
Rows: 16440 Columns: 9
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): sample, genotyper, celltype, gene
dbl (5): TE, seTE, lower, upper, n_cells

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
meta_df %>% 
  ggplot(aes(x=celltype,y=TE, fill = celltype))+
  geom_violin()+
  geom_jitter(alpha = 0.2, size = 0.2)+
  stat_summary(fun=mean, stat= "point")+
  stat_summary(fun.data = mean_se, geom="errorbar")+
  facet_grid(gene~genotyper)+
  theme_bw()+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio of dominant allele", x= NULL, fill = "Cell type") +
  scale_fill_brewer(palette = "Dark2")


meta_df %>% 
  ggplot(aes(x=genotyper,y=TE, fill = celltype))+
  geom_violin()+
  geom_jitter(alpha = 0.2, size = 0.2)+
  stat_summary(fun=mean, stat= "point")+
  stat_summary(fun.data = mean_se, geom="errorbar")+
  facet_grid(gene~celltype)+
  theme_bw()+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio of dominant allele", x= NULL, fill = "Cell type") +
  scale_fill_brewer(palette = "Dark2")

 meta_df %>% 
  filter(sample == samp) %>%
  filter(celltype == "cDC") %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>%
  ggplot(aes(x = genotyper, y = TE, ymin = lower, ymax = upper))+
  geom_point()+
  geom_errorbar(width = 0.4)+
  facet_grid(.~gene)+
  theme_bw()+
  scale_y_continuous(limits = c(0,NA))+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio\nof dominant allele", x= NULL)

Correlation analysis

meta_df %>% 
  filter(gene == "A", celltype == "cDC") %>% 
  select(sample, genotyper, TE) %>% 
  pivot_wider(names_from = "genotyper", values_from = "TE") %>% 
  select(-sample) %>% 
  GGally::ggpairs(progress = FALSE) +
  theme_bw()

accuracy_df <- readRDS(here("3_DRB/isb_accuracy_drb345_filtered.RDS")) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  select(sample, gene=locus, genotyper, accuracy) %>% 
  distinct()
for (i in c(0,1)){
  suppressWarnings({
  plt <- meta_df %>% 
    ungroup() %>% 
    filter(gene == "A", celltype == "cDC") %>% 
    left_join(accuracy_df, by = c("sample", "gene", "genotyper")) %>% 
    filter(accuracy == i | genotyper == "Ground truth") %>% 
    # drop_na(accuracy) %>% 
    select(sample, genotyper, TE) %>% 
    pivot_wider(names_from = "genotyper", values_from = "TE") %>% 
    select(-sample) %>% 
    GGally::ggpairs(progress = FALSE) +
    theme_bw() +
    ggtitle(sprintf("Correlation of allele ratios where accuracy = %s", i))
  print(plt)
  })
}

corr_df <- meta_df %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  select(sample, genotyper, celltype, gene, TE) %>% 
  pivot_wider(names_from = "genotyper", values_from = "TE") %>% 
  pivot_longer(c(arcasHLA, HLAminer, OptiType, PHLAT), names_to = "genotyper", values_to = "TE") %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper))
corr_df %>% 
  ggplot(aes(x=`Ground truth`, y=TE))+
  geom_point(size = 0.5)+
  facet_grid(gene ~ genotyper) +
  theme_bw() +
  ggpubr::stat_cor(aes(label = ..rr.label..), label.x.npc = "left", label.y.npc = "top", geom = "label")

for (i in c(0,1)){
  suppressWarnings({
  plt <- corr_df %>% 
    ungroup() %>% 
    # filter(gene == "A", celltype == "cDC") %>% 
    left_join(accuracy_df, by = c("sample", "gene", "genotyper")) %>% 
    filter(accuracy == i) %>% 
    ggplot(aes(x=`Ground truth`, y=TE))+
    geom_point(size = 0.5)+
    facet_grid(gene ~ genotyper) +
    theme_bw() +
    ggpubr::stat_cor(aes(label = ..rr.label..), label.x.npc = "left", label.y.npc = "top", geom = "label") +
      ggtitle(sprintf("Correlation of allele ratios where accuracy = %s", i)) +
    labs(y = "Predicted genotype HLA allele log-odds ratio", x = "Ground truth genotype HLA allele log-odds ratio")
  assign(sprintf("plt_meta_corr_%s", i), plt)
  print(plt)
  })
}

plt_meta_corr_abbrev <- corr_df %>% 
  left_join(accuracy_df, by = c("sample", "gene", "genotyper")) %>% 
  filter(gene == "A", accuracy %in% c(0,0.5,1)) %>%
  ggplot(aes(x=`Ground truth`, y=TE))+
    geom_point(size = 0.5)+
    facet_grid(accuracy ~ genotyper, labeller = labeller(accuracy = function(x) sprintf("Accuracy: %s", x))) +
    theme_bw() +
    ggpubr::stat_cor(aes(label = ..rr.label..), label.x.npc = "left", label.y.npc = "top", geom = "label", method = "pearson") +
    labs(y = "Log-odds ratio using predicted genotype", x = "Log-odds ratio using ground truth genotyper")
plt_meta_corr_abbrev
Warning: Removed 186 rows containing non-finite values (stat_cor).
Warning: Removed 186 rows containing missing values (geom_point).

accuracy_df %>% 
  ungroup() %>% 
  drop_na() %>% 
  count(gene, genotyper, accuracy)

By severity

meta_df %>% 
  filter(genotyper == "Ground truth") %>% 
  left_join(
    srt %>% 
      select(sample = sampleID, severity) %>% 
      distinct(),
    by = "sample"
  ) %>% 
  ggplot(aes(x=severity,y=TE, fill = celltype))+
    geom_violin()+
    geom_jitter(alpha = 0.2, size = 0.2)+
    stat_summary(fun=mean, stat= "point")+
    stat_summary(fun.data = mean_se, geom="errorbar")+
    facet_grid(gene~celltype)+
    theme_bw()+
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    labs(y = "Log-Odds ratio of dominant allele", x= NULL, fill = "Cell type") +
    scale_fill_brewer(palette = "Dark2")

meta_df %>% 
  filter(genotyper == "Ground truth") %>% 
  left_join(
    srt %>% 
      select(sample = sampleID, severity) %>% 
      distinct(),
    by = "sample"
  ) %>% 
  mutate(severity = as.numeric(severity)) %>% 
  ggplot(aes(x = severity, y = TE)) +
  # geom_point() + 
  stat_summary(fun=mean, stat= "point", size = 0.2)+
  stat_summary(fun.data = mean_se, geom="errorbar", size = 0.2)+
  stat_smooth(method = "lm")+
  facet_grid(celltype~gene)+
  theme_bw() 
`geom_smooth()` using formula 'y ~ x'

Plots for figures

Assemble plot

# plt_allele_freq_lgd <- cowplot::get_legend(plt_allele_freq)
# plt_max_allele_lgd <- cowplot::get_legend(plt_max_allele)
col_1 <- plot_grid(
  plt_allele_freq,
  plt_max_allele,
  ncol = 1,
  rel_heights = c(5,3),
  align = "v", axis = "lr",
  labels = LETTERS[1:2]
)

col_2 <- plot_grid(
  plt_srt,
  plt_meta,
  ncol = 1,
  align = "v", axis = "lr",
  labels = LETTERS[3:4],
  hjust = 0.5
)
row_1 <- plot_grid(
  col_1,
  col_2,
  nrow = 1,
  rel_widths = c(6,3)
)
row_2 <- plot_grid(
  plt_meta_corr_0 +ggtitle(NULL),
  plt_meta_corr_1 +ggtitle(NULL),
  labels = LETTERS[5:6],
  ncol = 2
)
Warning: Removed 4064 rows containing non-finite values (stat_cor).
Warning: Removed 4064 rows containing missing values (geom_point).
Warning: Removed 109 rows containing non-finite values (stat_cor).
Warning: Removed 109 rows containing missing values (geom_point).
plot_grid(
  row_1,
  row_2,
  rel_heights = c(3,2),
  ncol = 1
)

col_1 <- plot_grid(
  plt_srt,
  plt_meta,
  ncol = 1,
  align = "v", axis = "lr",
  labels = LETTERS[c(1,3)],
  label_x = -0.05
)

col_2 <- plot_grid(
  plt_max_allele,
  plt_meta_corr_abbrev,
  ncol = 1,
  rel_heights = c(3,5),
  align = "v", axis = "lr",
  labels = LETTERS[c(2,4)],
  label_x = -0.05
)
Warning: Removed 186 rows containing non-finite values (stat_cor).
Warning: Removed 186 rows containing missing values (geom_point).
plt_fig_main <- plot_grid(
  NULL,
  col_1,
  col_2,
  nrow = 1,
  rel_widths = c(0.25,3,6)
)

plt_fig_main

col_1 <- plot_grid(
  plt_srt,
  plt_meta+ labs(y="Log-odds ratio\nof dominant allele"),
  ncol = 1,
  align = "v", axis = "lr",
  labels = LETTERS[c(1,3)],
  label_x = -0.05
)

col_2 <- plot_grid(
  plt_max_allele,
  plt_meta_corr_abbrev + facet_grid(. ~ genotyper) + labs(y="Log-odds ratio using\npredicted genotype"),
  ncol = 1,
  rel_heights = c(1,1),
  align = "v", axis = "lr",
  labels = LETTERS[c(2,4)],
  label_x = -0.05
)
Warning: Removed 186 rows containing non-finite values (stat_cor).
Warning: Removed 186 rows containing missing values (geom_point).
plt_fig_main <- plot_grid(
  NULL,
  col_1,
  NULL,
  col_2,
  nrow = 1,
  rel_widths = c(0.25,3,0.25,6)
)

plt_fig_main

save_plot(here("figures_pdf/v2/7_scHLA.pdf"), plt_fig_main, base_height = 5, base_width = 14)
plt_fig_supp <- plt_meta_corr_abbrev + facet_grid(accuracy ~ genotyper, margins = "accuracy") 
plt_fig_supp
Warning: Removed 372 rows containing non-finite values (stat_cor).
Warning: Removed 372 rows containing missing values (geom_point).

save_plot(here("figures_pdf/v2/s8_ASE_correlation.pdf"), plt_fig_supp, base_height = 5, base_width = 6)
Warning: Removed 372 rows containing non-finite values (stat_cor).
Warning: Removed 372 rows containing missing values (geom_point).
plt_fig_supp_allLoci <- plot_grid(
  plt_max_allele_allGenesTypers,
  plt_meta_allLoci,
  ncol = 1,
  rel_heights = c(0.8,0.2), 
  align = "v",
  axis = "lr",
  labels = LETTERS[1:2]
)

plt_fig_supp_allLoci

save_plot(here("figures_pdf/v2/s7_ASE_allLoci.pdf"), plt_fig_supp_allLoci, base_height = 10, base_width = 12)

Save all figure objects

listN <- function(...) {
  anonList <- list(...)
  names(anonList) <- as.character(substitute(list(...)))[-1]
  anonList
}

saveRDS(
  listN(plt_srt,
        plt_meta,
        plt_max_allele,
        plt_meta_corr_abbrev
        ),
  here("figures_object/7_scHLA_objects.RDS")
)

Old work

LS0tCnRpdGxlOiAiNykgRWZmZWN0IG9mIGdlbm90eXBlcnMgb24gSExBIGFsbGVsZS1zcGVjaWZpYyBleHByZXNzaW9uIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgojIFNldHVwCmBgYHtyLCBtZXNzYWdlID0gRn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobWV0YSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkoZ2dyYXN0cikKCnNvdXJjZShoZXJlKCJoZWxwZXJfZnVuY3Rpb25zL2RhdGFfaW1wb3J0X2Z1bmN0aW9ucy5SIikpCnNvdXJjZShoZXJlKCJoZWxwZXJfZnVuY3Rpb25zL2ZpZ3VyZV9mb3JtYXRfZnVuY3Rpb25zLlIiKSkKc291cmNlKGhlcmUoImhlbHBlcl9mdW5jdGlvbnMvY2FsY3VsYXRpb25fZnVuY3Rpb25zLlIiKSkKYWxsX2hsYV9leHBhbmRlZCA8LSByZWFkUkRTKGhlcmUoIjJfYWNjdXJhY3kvYWxsX2hsYV9leHBhbmRlZC5SRFMiKSkKaXNiX3BhdGggPC0gaGVyZSgiZGF0YS9pc2IiKQpgYGAKCgojIFJ1bm5pbmcgc2NITEEgY291bnQKCi0gKipOT1RFOioqIEZpbGVwYXRocyBmb3IgcnVubmluZyBzY0hMQWNvdW50IG9uIHJhdyBzZXF1ZW5jaW5nIGRhdGEgaGF2ZSBub3QgYmVlbiByZWZhY3RvcmVkIGZvciBnaXRodWIgcmVwby4gQWxsIG91dHB1dCBmaWxlcyByZWxldmFudCBmb3IgZG93bnN0cmVhbSBhbmFseXNpcyBoYXZlIGJlZW4gbW92ZWQgdG8gYGRhdGEvaXNiL3NjSExBY291bnRgIHdpdGhpbiB0aGUgZ2l0aHViIHJlcG8uICAKCiMjIyBQcmVwYXJlIHJlZmVyZW5jZSBnZW5vdHlwZSBmaWxlcwoKYGBge3IsIGV2YWw9Rn0KIyBBc3NlbWJsZSBrZXkgbGlua2luZyBzYW1wbGUtcG9vbCB0byBzaW1wbGlmaWVkIHNhbXBsZQpobGFfc2FtcGxlcyA8LSByZWFkX3RzdihoZXJlKCJkYXRhL2lzYi9zY0hMQWNvdW50L0JMX2Zhc3RxX2ZpbGVzLnR4dCIpLCBjb2xfbmFtZXMgPSAiZmlsZSIpICU+JSAKICBmaWx0ZXIoZ3JlcGwoIl5JTkNPViIsIGZpbGUpKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCItQkwiLCBmaWxlKSkgJT4lIAogIHNlcGFyYXRlKGZpbGUsIGludG8gPSBjKCJzYW1wbGUiLCBOQSksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRikgJT4lIAogIGRyb3BfbmEoKQoKIyBGcm9tIGFsbCBnZW5vdHlwZSBmaWVsZCByZXN1bHRzLCBhc3NlbWJsZSBoaWdoZXN0IHJlc29sdXRpb24gZ2Vub3R5cGUgZm9yIGFsbCBzYW1wbGU6Z2Vub3R5cGVycwpzZWxlY3RfbGFzdF9hbGxlbGUgPC0gZnVuY3Rpb24oeCl7CiAgeFshaXMubmEoeCldICU+JQogICAgdGFpbCgxKSAlPiUgCiAgICBzdHJfcmVwbGFjZV9hbGwoIl8iLCAiOiIpfQpobGFfa2V5IDwtIGFsbF9obGFfZXhwYW5kZWQgJT4lIAogIHJvd3dpc2UoKSAlPiUgCiAgZmlsdGVyKGxvY3VzICVpbiUgYygiQSIsIkIiLCJDIiwiRFBBMSIsIkRQQjEiLCJEUUExIiwiRFFCMSIsIkRSQjEiKSkgJT4lIAogIG11dGF0ZShhbGxlbGUgPSBzZWxlY3RfbGFzdF9hbGxlbGUoYWNyb3NzKGNvbnRhaW5zKCJmaWVsZCIpKSkpICU+JSAKICBzZWxlY3Qoc2FtcGxlLCBnZW5vdHlwZXIsIGxvY3VzLCBhbGxlbGUpICU+JSAKICB1bml0ZShhbGxlbGUsIGxvY3VzLCBhbGxlbGUsIHNlcCA9ICIqIikKCiMgTWVyZ2UgaW50byBrZXkgb2YgbGlzdCBvZiBnZW5vdHlwZXMgZm9yIGVhY2ggc2FtcGxlLXBvb2w6Z2Vub3R5cGVyIHBhaXIKaGxhX21lcmdlIDwtIGhsYV9zYW1wbGVzICU+JSAKICBsZWZ0X2pvaW4oaGxhX2tleSwgYnkgPSAic2FtcGxlIikgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgc2VsZWN0KC1zYW1wbGUpICU+JSAKICBncm91cF9ieShmaWxlLCBnZW5vdHlwZXIpICU+JSAKICBuZXN0KCkKCiMgIyBXcml0ZSBrZXlzIHRvIHNldCBvZiBjc3ZzIGZvciBpbnB1dCB0byBzY0hMQWNvdW50CiMgaGxhX21lcmdlICU+JQojICAgbXV0YXRlKHdyaXRlID0gcG1hcChsaXN0KGRhdGEsIGZpbGUsIGdlbm90eXBlciksIGZ1bmN0aW9uKGQsZixnKXsKIyAgICAgZGlyIDwtIGhlcmUoc3ByaW50ZigiZGF0YS9pc2Ivc2NITEFjb3VudC9nZW5vdHlwZXMvJXMiLGcpKQojICAgICBpZiAoIWRpci5leGlzdHMoZGlyKSl7ZGlyLmNyZWF0ZShkaXIsIHJlY3Vyc2l2ZSA9IFQpfQojICAgICB3cml0ZV90c3YoZCwKIyAgICAgICAgICAgICAgIHNwcmludGYoIiVzLyVzX2hsYS50c3YiLGRpcixmKSwKIyAgICAgICAgICAgICAgIGNvbF9uYW1lcyA9IEYsCiMgICAgICAgICAgICAgICApfSkpCmBgYAoKCiMjIyBSdW4gc2NyaXB0Cgpgc2JhdGNoIC9jb3ZpZC9zY3JpcHRzL2lzYl9zY0hMQWNvdW50X2JlbmNobWFyay5zaGAKCmBgYHtiYXNoLCBldmFsPUZ9CiMhL2Jpbi9zaAoKIyBTRVQgR0xPQkFMIFZBUklBQkxFUwojIEdlbmVyYWwKZXhwb3J0IEdJVF9ESVI9L2xhYnMva2hhdHJpbGFiL3NvbG9tb25iL2hsYV9wcm9qZWN0L2hsYV9iZW5jaG1hcmsvZGF0YS9pc2Ivc2NITEFjb3VudApleHBvcnQgQkFTRV9ESVI9L2xhYnMva2hhdHJpbGFiL3NvbG9tb25iL2NvdmlkL2lzYi9zY0hMQWNvdW50CmV4cG9ydCBMT0dfRElSPSRHSVRfRElSL2xvZ3MvJChkYXRlICsnJXklbSVkXyVIJU0lUycpCiMgRkFTVFEvSElTQVQKZXhwb3J0IElOREVYX0RJUj0vbGFicy9raGF0cmlsYWIvc29sb21vbmIvcm5hc2VxX3Byb2Nlc3NpbmcvaGlzYXQyL2hpc2F0X2FyY2FzL2hpc2F0X2RhdGEvZ3JjaDM4CmV4cG9ydCBCQU1fRElSPSRCQVNFX0RJUi9iYW0KIyBITEEgcmVmZXJlbmNlcwpleHBvcnQgSExBX0RJUj0kR0lUX0RJUi9obGFfcmVmZXJlbmNlcwpleHBvcnQgSExBTlVDPS9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9yZWZlcmVuY2VzL0lNR1RITEEvaGxhX251Yy5mYXN0YQpleHBvcnQgSExBR0VOPS9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9yZWZlcmVuY2VzL0lNR1RITEEvaGxhX2dlbi5mYXN0YQojIFNDSExBCmV4cG9ydCBCQVJDT0RFX0RJUj0kR0lUX0RJUi9iYXJjb2RlcwpleHBvcnQgR0VOT1RZUEVfRElSPSRHSVRfRElSL2dlbm90eXBlcwpleHBvcnQgU0NITEFDT1VOVF9ESVI9JEdJVF9ESVIvb3V0cHV0CmV4cG9ydCBURU1QX0RJUj0kR0lUX0RJUi90ZW1wX2Zhc3RxCiMgU0xVUk0gCmV4cG9ydCBOX0NPUkVTPSRTTFVSTV9DUFVTX1BFUl9UQVNLCgoKIyBDUkVBVEUgRElSRUNUT1JJRVMKaWYgWyAhIC1kICRMT0dfRElSIF07IHRoZW4gbWtkaXIgLXAgJExPR19ESVI7ZmkKaWYgWyAhIC1kICRCQU1fRElSIF07IHRoZW4gbWtkaXIgLXAgJEJBTV9ESVI7ZmkKaWYgWyAhIC1kICRTQ0hMQUNPVU5UX0RJUiBdOyB0aGVuIG1rZGlyIC1wICRTQ0hMQUNPVU5UX0RJUjtmaQppZiBbICEgLWQgJFRFTVBfRElSIF07IHRoZW4gbWtkaXIgLXAgJFRFTVBfRElSO2ZpCgojIENSRUFURSBITEEgUkVGRVJFQ0UgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpITEFfUkVGRVJFTkNFKCl7CiAgc291cmNlIC9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9taW5pY29uZGEzL2V0Yy9wcm9maWxlLmQvY29uZGEuc2gKICBjb25kYSBhY3RpdmF0ZSBzYW10b29scwogIAogIHByaW50ZiAiXG5cCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIENSRUFURSBSRUZFUkVOQ0UgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjXAogIFxuIiA+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICBwcmludGYgIlxuIyMjIFtTVEFSVF9fX1JFRkVSRU5DRV9fXyQoZGF0ZSArJyVEICVYJyldXG4iID4+ICRMT0dfRElSLyR7MX0vJHsxfV8kezJ9LmxvZwogIAogIFsgISAtZCAkSExBX0RJUi8kezJ9IF0gJiYgbWtkaXIgLXAgJEhMQV9ESVIvJHsyfQogIAogIHdoaWxlIHJlYWQgLXIgbGluZTsgZG8gZ3JlcCAtRiAtbSAxICRsaW5lICRITEFOVUMgPj4gJEhMQV9ESVIvJHsyfS8kezF9X3RtcGFsbGVsZS50eHQ7IGRvbmUgPCAkR0VOT1RZUEVfRElSLyR7Mn0vJHsxfV9obGEudHN2CiAgCiAgc2FtdG9vbHMgZmFpZHggICRITEFOVUMgJChjdXQgLWYxIC1kJyAnICRITEFfRElSLyR7Mn0vJHsxfV90bXBhbGxlbGUudHh0IHwgdHIgJz4nICcgJyB8IHRyICdcbicgJyAnKSA+ICRITEFfRElSLyR7Mn0vJHsxfV9jZHMuZmFzdGEgMj4+ICRMT0dfRElSLyR7MX0vJHsxfV8kezJ9LmxvZwogIHNhbXRvb2xzIGZhaWR4ICAkSExBR0VOICQoY3V0IC1mMSAtZCcgJyAkSExBX0RJUi8kezJ9LyR7MX1fdG1wYWxsZWxlLnR4dCB8IHRyICc+JyAnICcgfCB0ciAnXG4nICcgJykgPiAkSExBX0RJUi8kezJ9LyR7MX1fZ2VuLmZhc3RhIDI+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICAKICB3aGlsZSByZWFkIC1yIGxpbmU7IGRvIElGUz0nICc7IHJlYWQgLXIgZjEgZjIgPDw8IiRsaW5lIjsgc2VkIC1pIiIgInMvJGYxLyRmMSAkZjIvZyIgJEhMQV9ESVIvJHsyfS8kezF9X2Nkcy5mYXN0YTsgZG9uZSA8ICRITEFfRElSLyR7Mn0vJHsxfV90bXBhbGxlbGUudHh0IDI+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICB3aGlsZSByZWFkIC1yIGxpbmU7IGRvIElGUz0nICc7IHJlYWQgLXIgZjEgZjIgZjMgPDw8IiRsaW5lIjsgc2VkIC1pIiIgInMvJGYxLyRmMSAkZjIvZyIgJEhMQV9ESVIvJHsyfS8kezF9X2dlbi5mYXN0YTsgZG9uZSA8ICRITEFfRElSLyR7Mn0vJHsxfV90bXBhbGxlbGUudHh0IDI+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICAKICBybSAkSExBX0RJUi8kezJ9LyR7MX1fdG1wYWxsZWxlLnR4dAogIHByaW50ZiAiIyMjIFtDT01QTEVURV9fX1JFRkVSRU5DRV9fXyQoZGF0ZSArJyVEICVYJyldXG4iID4+ICRMT0dfRElSLyR7MX0vJHsxfV8kezJ9LmxvZwp9CmV4cG9ydCAtZiBITEFfUkVGRVJFTkNFCgoKIyBERUZJTkUgc2NITEEgR0VOT1RZUElORyBQSVBFTElORSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKU0NITEFDT1VOVCgpewogIHNvdXJjZSAvbGFicy9raGF0cmlsYWIvc29sb21vbmIvbWluaWNvbmRhMy9ldGMvcHJvZmlsZS5kL2NvbmRhLnNoCiAgY29uZGEgYWN0aXZhdGUgc2FtdG9vbHMKICAKICBwcmludGYgIlxuXAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyBSVU4gU0NITEFDT1VOVCAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyNcCiAgXG4iID4+ICRMT0dfRElSLyR7MX0vJHsxfV8kezJ9LmxvZwogIHByaW50ZiAiXG4jIyMgW1NUQVJUX19fU0NITEFDT1VOVF9fXyQoZGF0ZSArJyVEICVYJyldXG4iID4+ICRMT0dfRElSLyR7MX0vJHsxfV8kezJ9LmxvZwogIAogIGVjaG8gIiMjIyBTdGFydGluZyAgc2NITEFjb3VudCBhdCAkKGRhdGUgKyclRCAlWCcpIiA+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICAKICAjIGlmIFsgLWQgJFNDSExBQ09VTlRfRElSLyR7MX1fcmVzdWx0cyBdOyB0aGVuIHJtIC1yICRTQ0hMQUNPVU5UX0RJUi8kezF9X3Jlc3VsdHM7ZmkKICBbICEgLWQgU0NITEFDT1VOVF9ESVIvJHsyfSBdICYmIG1rZGlyIC1wICRTQ0hMQUNPVU5UX0RJUi8kezJ9CgogIHNjX2hsYV9jb3VudCBcCiAgLS1iYW0gJEJBTV9ESVIvJHsxfS5iYW0gXAogIC0tY2VsbC1iYXJjb2RlcyAkQkFSQ09ERV9ESVIvJHsxfV9iYXJjb2RlLnRzdiBcCiAgLS1vdXQtZGlyICRTQ0hMQUNPVU5UX0RJUi8kezJ9LyR7MX1fcmVzdWx0cyBcCiAgLS1mYXN0YS1jZHMgJEhMQV9ESVIvJHsyfS8kezF9X2Nkcy5mYXN0YSBcCiAgLS1mYXN0YS1nZW5vbWljICRITEFfRElSLyR7Mn0vJHsxfV9nZW4uZmFzdGFcCiAgPj4gJExPR19ESVIvJHsxfS8kezF9XyR7Mn0ubG9nIFwKICAyPj4gJExPR19ESVIvJHsxfS8kezF9XyR7Mn0ubG9nCgogIHByaW50ZiAiIyMjIFtDT01QTEVURV9fX1NDSExBQ09VTlRfX18kKGRhdGUgKyclRCAlWCcpXVxuIiA+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKfQpleHBvcnQgLWYgU0NITEFDT1VOVAoKCiMgREVGSU5FIFBJUEVMSU5FIENPTlRST0xMRVIgRlVOQ1RJT04gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjClBJUEVMSU5FKCl7CiAgZWNobyAiU1RBUlQ6IHNhbXBsZSAkMSBhdCAkKGRhdGUgKyclRCAlWCcpIgogIGZvciBHIGluIGFyY2FzSExBIGhsYW1pbmVyIGludml0cm8gb3B0aXR5cGUgcGhsYXQKICBkbwogICAgWyAhIC1kICRMT0dfRElSLyR7MX0gXSAmJiBta2RpciAtcCAkTE9HX0RJUi8kezF9CiAgICBITEFfUkVGRVJFTkNFICQxICRHCiAgICBTQ0hMQUNPVU5UICQxICRHCiAgZG9uZQogIGVjaG8gIkNPTVBMRVRFOiBzYW1wbGUgJDEgYXQgJChkYXRlICsnJUQgJVgnKSIKfQpleHBvcnQgLWYgUElQRUxJTkUKCmNhdCAkR0lUX0RJUi9CTF9mYXN0cV9maWxlcy50eHQgfCBwYXJhbGxlbCAtLWRlbGF5IDE1IC1qICRTTFVSTV9OVEFTS1MgLS1qb2Jsb2cgJExPR19ESVIvcGFyYWxsZWwubG9nIFBJUEVMSU5FIHt9CmBgYAoKCiMgVU1BUCBwcm9qZWN0aW9ucwoKIyMjIENlbGwgY2x1c3RlcnMKCmBgYHtyfQojIHNydCA8LSByZWFkUkRTKCIvbGFicy9raGF0cmlsYWIvaG9uZ3poZW5nL3dlYmFwcHMvaXNiL3NydF9pc2IyNjAubWV0YS5yZHMiKQpzcnQgPC0gcmVhZFJEUyhoZXJlKCJkYXRhL2lzYi9pc2Jfc2NfbWV0YWRhdGEuUkRTIikpCmNlbGxzIDwtIGMoIkNEMTQgTW9ub2N5dGUiLCAiQ0Q0IFQiLCAiQ0Q4IFQiLCAiTksiLCAiQiIsICJDRDE2IE1vbm9jeXRlIiwgImNEQyIsICJwREMiKQpzcnQgPC0gc3J0ICU+JSBzZWxlY3Qoc2FtcGxlLCBzYW1wbGVJRCwgY2VsbCwgY2VsbHR5cGUsIHNldmVyaXR5LCBVTUFQXzEsIFVNQVBfMikgJT4lIAogIGZpbHRlcihjZWxsdHlwZSAlaW4lIGNlbGxzKQpgYGAKCmBgYHtyfQojIGdncGxvdCB0aGVtZSB0byBvdmVybGF5IGNsdXN0ZXIgaWQgI3Mgb24gVU1BUApnZ19zcnRfcmVsYWJlbCA8LSBmdW5jdGlvbihkZiwgeF92YXIsIHlfdmFyLCBjb2xvcl92YXIsIGNlbGxfZnJhY3Rpb24gPSAxKXsKICBwbHQgPC0gZGYgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gISFzeW0oeF92YXIpLCB5ID0gISFzeW0oeV92YXIpLCBjb2xvciA9ICEhc3ltKGNvbG9yX3ZhcikpKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGFscGhhID0gMC41KSsKICAgIHRoZW1lX2J3KCkgKwogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSsKICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAyLCBhbHBoYSA9IDEpKSkKICAKICBnIDwtIGdncGxvdF9idWlsZChwbHQpCiAgCiAgcGx0X2lkcyA8LSBnJGRhdGFbWzFdXQogIGdyb3VwX2xldmVscyA8LSBsZXZlbHMoZmFjdG9yKGckcGxvdCRkYXRhW1tnJHBsb3QkbGFiZWxzJGNvbG91cl1dKSkKICAKICBwbHRfa2V5IDwtIGckZGF0YVtbMV1dICU+JSAKICAgIHNlbGVjdChjb2xvdXIsIGdyb3VwKSAlPiUgCiAgICBkaXN0aW5jdCgpICU+JSAKICAgIG11dGF0ZShsYWJlbCA9IG1hcF9jaHIoZ3JvdXAsIGZ1bmN0aW9uKHgpIGdyb3VwX2xldmVsc1t4XSkpICU+JSAKICAgIG11dGF0ZShsYWJlbCA9IGZhY3RvcihsYWJlbCwgbGV2ZWwgPSBncm91cF9sZXZlbHMpKSAlPiUKICAgIG11dGF0ZShsYWJlbCA9IHNwcmludGYoIiVzKSAlcyIsIDE6bigpLCBsYWJlbCkpICU+JSAKICAgIHNlbGVjdCgtZ3JvdXApCiAgCiAgcGx0X2RmIDwtIHBsdF9pZHMgJT4lIAogICAgbGVmdF9qb2luKHBsdF9rZXksIGJ5ID0gImNvbG91ciIpCiAgCiAgcGx0X2NlbnRlciA8LSBwbHRfZGYgJT4lIAogICAgZ3JvdXBfYnkobGFiZWwpICU+JSBzdW1tYXJpc2UoeCA9IG1lYW4oeCksIHkgPSBtZWFuKHkpKSAlPiUKICAgIG11dGF0ZShsYWJlbCA9IGdzdWIoIikuKiIsIiIsbGFiZWwpKQogIAogIHBsdF9yZXBlbCA8LSBwbHRfZGYgJT4lIAogICAgZ2dwbG90KGFlcyh4PXgseT15LGNvbG9yPWxhYmVsKSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gMC41KSsKICAgIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhPXBsdF9jZW50ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsPWxhYmVsLCAgYmcuY29sb3I9IndoaXRlIiwgYmcucj0wLjI1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIpICsKICAgIHRoZW1lX2J3KCkgKwogICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDIpICkgKSArCiAgICBsYWJzKHg9eF92YXIsIHkgPSB5X3ZhciwgY29sb3IgPSBjb2xvcl92YXIpICsKICAgIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkKICByZXR1cm4ocGx0X3JlcGVsKQp9CgpwbHRfc3J0IDwtIHNydCAlPiUgCiAgZmlsdGVyKGNlbGx0eXBlICVpbiUgY2VsbHMpICU+JSAKICBmaWx0ZXIoc2FtcGxlSUQgPT0gIklOQ09WMDY5LUJMIikgJT4lIAogIGdnX3NydF9yZWxhYmVsKHhfdmFyID0gIlVNQVBfMSIsIHlfdmFyID0gIlVNQVBfMiIsIGNvbG9yX3ZhciA9ICJjZWxsdHlwZSIsIGNlbGxfZnJhY3Rpb24gPSAwLjEpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpKyAKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHg9IlVNQVAgMSIsIHkgPSAiVU1BUCAyIiwgY29sb3IgPSAiQ2VsbCBDbHVzdGVyIikKcGx0X3NydApgYGAKIyMjIEFsbGVsZSBmcmVxdWVuY3kKCmBgYHtyfQojIHNhbXAgPC0gIklOQ09WMDY5LUJMIgojIHNhbXAgPC0gIklOQ09WMDIyLUJMIgojIHNhbXAgPC0gIklOQ09WMDgzLUJMIgpzYW1wIDwtICJJTkNPVjAyOC1CTCIKc2NITEFjb3VudF9kaXIgPC0gc3ByaW50ZigiJXMvc2NITEFjb3VudCIsIGlzYl9wYXRoKQoKIyBDcmVhdGUgdGliYmxlIG9mIGFsbCBnZW5vdHlwZXIgYW5kIHNhbXBsZSBjb21iaW5hdGlvbnMKYWxsZWxlX2RhdGEgPC0gZXhwYW5kX2dyaWQoCiAgZ2Vub3R5cGVyID0gYygiaW52aXRybyIsICJhcmNhc0hMQSIsICJvcHRpdHlwZSIsICJwaGxhdCIsICJobGFtaW5lciIpLAogIHNhbXBsZSA9IHJlYWRfbGluZXMoaGVyZSgiZGF0YS9pc2Ivc2NITEFjb3VudC9CTF9mYXN0cV9maWxlcy50eHQiKSkpICU+JSAKICBmaWx0ZXIoZ3JlcGwoc2FtcCwgc2FtcGxlKSkgJT4lIAogICMgSW1wb3J0IGRhdGEgYmFzZWQgb24gc2FtcGxlIGFuZCBnZW5vdHlwZXIKICBtdXRhdGUocmVzdWx0X3BhdGggPSBzcHJpbnRmKCIlcy9zY0hMQWNvdW50L291dHB1dC8lcyIsaXNiX3BhdGgsIGdlbm90eXBlciksCiAgICAgICAgIGJhcmNvZGVfcGF0aCA9IHNwcmludGYoIiVzL3NjSExBY291bnQvYmFyY29kZXMiLCBpc2JfcGF0aCkpICU+JSAKICBtdXRhdGUoZGF0YSA9IHBtYXAobGlzdChzYW1wbGUsIHJlc3VsdF9wYXRoLCBiYXJjb2RlX3BhdGgpLCBmdW5jdGlvbihzLHIsYil7CiAgICBkZiA8LSBzY0hMQV9kYXRhX3Byb2Nlc3NpbmcoCiAgICAgIHNhbXBsZT1zLAogICAgICByZXN1bHRfZGlyPXIsCiAgICAgIGJhcmNvZGVfZGlyPWIKICAgICkgCiAgfSkpICU+JSB1bm5lc3QoZGF0YSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQpwbG90X2FsbGVsZV9mcmVxdWVuY2llcyA8LSBmdW5jdGlvbihhbGxlbGVfZGF0YSwgZ2VuZV9zZWxlY3QpewoKICBhbGxlbGVfZGF0YV9yYXRpb3MgPC0gYWxsZWxlX2RhdGEgJT4lIAogICAgZmlsdGVyKGdlbmUgPT0gZ2VuZV9zZWxlY3QpICU+JSAKICAgIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAKICAgIG11dGF0ZShhbGxlbGVfb3JkZXIgPSBmY3RfcmVjb2RlKGZhY3RvcihhbGxlbGVfb3JkZXIpLCAiQWxsZWxlIDEiID0gIjEiLCAiQWxsZWxlIDIiID0gIjIiKSkKICAgIAogIGFsbGVsZV9sYWJlbCA8LSBhbGxlbGVfZGF0YV9yYXRpb3MgJT4lIAogICAgc2VsZWN0KGdlbm90eXBlciwgYWxsZWxlX29yZGVyLCBhbGxlbGUpICU+JSAKICAgIGRpc3RpbmN0KCkKICAKICBwbHQgPC0gc3J0ICU+JSAKICAgIGxlZnRfam9pbihhbGxlbGVfZGF0YV9yYXRpb3MgJT4lIGZpbHRlcihnZW5lID09IGdlbmVfc2VsZWN0KSwgYnkgPSAiY2VsbCIpICU+JSAKICAgIGZpbHRlcighaXMubmEoYWxsZWxlKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGFsbGVsZV9yYXRpbykpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjUpKwogICAgdGhlbWVfYncoKSArCiAgICBmYWNldF9ncmlkKGFsbGVsZV9vcmRlcn5nZW5vdHlwZXIpKwogICAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGhpZ2ggPSAicmVkIiwgbWlkID0gImdyZXk4MCIsIGxvdyA9ICJibHVlIiwgbWlkcG9pbnQgPSAwLjUsIG5hLnZhbHVlID0gInRyYW5zcGFyZW50IikgKwogICAgbGFicyh4PSJVTUFQIDEiLCB5PSJVTUFQIDIiLCBjb2xvciA9ICJBbGxlbGUgXG5GcmVxdWVuY3kiKSsKICAgIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkrCiAgICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTEwLDE1KSkKICAKICBwbHRfYWxsZWxlX2ZyZXEgPC0gcGx0KwogICAgZ2VvbV9sYWJlbChkYXRhID0gYWxsZWxlX2xhYmVsLCBhZXMoeD0tMTAseT0xNCwgY29sb3IgPSBOVUxMLCBsYWJlbCA9IGFsbGVsZSksIHNpemUgPSAzLCBoanVzdCA9IDApCiAgCiAgcmV0dXJuKHBsdF9hbGxlbGVfZnJlcSkKfQoKcGx0X2FsbGVsZV9mcmVxIDwtIHBsb3RfYWxsZWxlX2ZyZXF1ZW5jaWVzKGFsbGVsZV9kYXRhID0gYWxsZWxlX2RhdGEsIGdlbmVfc2VsZWN0ID0gIkEiKQpwbHRfYWxsZWxlX2ZyZXEKYGBgCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9CnBsb3RfYWxsZWxlX2ZyZXF1ZW5jaWVzKGFsbGVsZV9kYXRhID0gYWxsZWxlX2RhdGEsIGdlbmVfc2VsZWN0ID0gIkRSQjEiKQpgYGAKCiMjIyBNYXhpbXVtIGFsbGVsZQoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpwbG90X2FsbGVsZV9yYXRpb3MgPC0gZnVuY3Rpb24oYWxsZWxlX2RhdGEsIGdlbmVfc2VsZWN0KXsKICAjIENyZWF0ZSB0aWJibGUgb2YgYWxsIGdlbm90eXBlciBhbmQgc2FtcGxlIGNvbWJpbmF0aW9ucwogIGFsbGVsZV9tYXhfcmF0aW8gPC0gYWxsZWxlX2RhdGEgICU+JQogICAgc2VsZWN0KGdlbm90eXBlciwgY2VsbCwgZ2VuZSwgYWxsZWxlX29yZGVyLCBhbGxlbGVfcmF0aW8pICU+JQogICAgIyBncm91cF9ieShnZW5vdHlwZXIsIGNlbGwsIGFsbGVsZV9vcmRlcikgJT4lICBtdXRhdGUodGVzdCA9IGxlbmd0aChhbGxlbGVfb3JkZXIpKQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJhbGxlbGVfb3JkZXIiLCB2YWx1ZXNfZnJvbSA9ICJhbGxlbGVfcmF0aW8iLCBuYW1lc19wcmVmaXggPSAiYWxsZWxlXyIpICU+JQogICAgcm93d2lzZSgpICU+JQogICAgbXV0YXRlKG1heF9yYXRpbyA9IG1heChhY3Jvc3MoY29udGFpbnMoImFsbGVsZV8iKSksIG5hLnJtID0gVCkpICU+JQogICAgc2VsZWN0KGdlbm90eXBlciwgY2VsbCwgZ2VuZSwgbWF4X3JhdGlvKSAlPiUKICAgIGZpbHRlcihnZW5lID09IGdlbmVfc2VsZWN0KSAKICAKICBhbGxlbGVfbGFiZWwgPC0gYWxsZWxlX2RhdGEgJT4lIHNlbGVjdChzYW1wbGUsIGdlbm90eXBlciwgZ2VuZSwgYWxsZWxlKSAlPiUgCiAgICBmaWx0ZXIoZ2VuZSA9PSBnZW5lX3NlbGVjdCkgJT4lIAogICAgc2VwYXJhdGUoc2FtcGxlLCBpbnRvID0gYygic2FtcGxlIiwgTlVMTCksIHNlcCA9ICJfIiwgZXh0cmEgPSAiZHJvcCIpICU+JSAKICAgIGRpc3RpbmN0KCkgJT4lIAogICAgZ3JvdXBfYnkoc2FtcGxlLCBnZW5vdHlwZXIsIGdlbmUpICU+JSBuZXN0KCkgJT4lIHVubmVzdF93aWRlcihkYXRhKSAlPiUgCiAgICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgCiAgICBtdXRhdGUoYWxsZWxlID0gbWFwX2NocihhbGxlbGUsIHBhc3RlLCBjb2xsYXBzZSA9ICJcbiIpKQogIAogIHBsdF9tYXhfYWxsZWxlIDwtIHNydCAlPiUgCiAgICBsZWZ0X2pvaW4oYWxsZWxlX21heF9yYXRpbyAlPiUgZmlsdGVyKGdlbmUgPT0gZ2VuZV9zZWxlY3QpLCBieSA9ICJjZWxsIikgJT4lIAogICAgIyBtdXRhdGUoZ2Vub3R5cGVyID0gZmN0X3JlbGV2ZWwoZ2Vub3R5cGVyLCAiaW52aXRybyIpKSAlPiUgCiAgICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgCiAgICBmaWx0ZXIoIWlzLm5hKGdlbm90eXBlcikpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IG1heF9yYXRpbykpICsKICAgICAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuNSkrCiAgICAgIGdlb21fbGFiZWwoZGF0YSA9IGFsbGVsZV9sYWJlbCwgYWVzKGNvbG9yID0gTlVMTCwgbGFiZWwgPSBhbGxlbGUsIHggPSAtSW5mLCB5ID0gLUluZiksCiAgICAgICAgICAgICAgaGp1c3QgPSAtMC4xLCB2anVzdCA9IC0wLjEsIHNpemUgPSAzLCBoanVzdCA9IDApICsKICAgICAgdGhlbWVfYncoKSArCiAgICAgIGZhY2V0X2dyaWQoLn5nZW5vdHlwZXIpKwogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudChoaWdoID0gInJlZCIsIGxvdyA9ICJibHVlIiwgbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArCiAgICAgIGxhYnMoeD0iVU1BUCAxIiwgeT0iVU1BUCAyIiwgY29sb3IgPSAiTWF4aW11bSBBbGxlbGUgXG5GcmVxdWVuY3kiKSsKICAgICAgdGhlbWUoYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpKQogIAogIHJldHVybihwbHRfbWF4X2FsbGVsZSkKfQoKcGx0X21heF9hbGxlbGUgPC0gcGxvdF9hbGxlbGVfcmF0aW9zKGFsbGVsZV9kYXRhID0gYWxsZWxlX2RhdGEsIGdlbmVfc2VsZWN0ID0gIkEiKQpwbHRfbWF4X2FsbGVsZQpgYGAKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpwbG90X2FsbGVsZV9yYXRpb3MoYWxsZWxlX2RhdGEgPSBhbGxlbGVfZGF0YSwgZ2VuZV9zZWxlY3QgPSAiRFJCMSIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQphbGxlbGVfbWF4X3JhdGlvIDwtIGFsbGVsZV9kYXRhICAlPiUKICAgIGZpbHRlcihnZW5vdHlwZXIgPT0gImludml0cm8iKSAlPiUgIAogICAgc2VsZWN0KGNlbGwsIGdlbmUsIGFsbGVsZV9vcmRlciwgYWxsZWxlX3JhdGlvKSAlPiUKICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiYWxsZWxlX29yZGVyIiwgdmFsdWVzX2Zyb20gPSAiYWxsZWxlX3JhdGlvIiwgbmFtZXNfcHJlZml4ID0gImFsbGVsZV8iKSAlPiUKICAgIHJvd3dpc2UoKSAlPiUKICAgIG11dGF0ZShtYXhfcmF0aW8gPSBtYXgoYWNyb3NzKGNvbnRhaW5zKCJhbGxlbGVfIikpLCBuYS5ybSA9IFQpKSAlPiUKICAgIHNlbGVjdChjZWxsLCBnZW5lLCBtYXhfcmF0aW8pCiAgCmFsbGVsZV9sYWJlbCA8LSBhbGxlbGVfZGF0YSAlPiUgIAogIGZpbHRlcihnZW5vdHlwZXIgPT0gImludml0cm8iKSAlPiUgCiAgc2VsZWN0KHNhbXBsZSwgZ2VuZSwgYWxsZWxlKSAlPiUKICBzZXBhcmF0ZShzYW1wbGUsIGludG8gPSBjKCJzYW1wbGUiLCBOVUxMKSwgc2VwID0gIl8iLCBleHRyYSA9ICJkcm9wIikgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIGdyb3VwX2J5KHNhbXBsZSwgZ2VuZSkgJT4lIG5lc3QoKSAlPiUgdW5uZXN0X3dpZGVyKGRhdGEpICU+JSAKICBtdXRhdGUoYWxsZWxlID0gbWFwX2NocihhbGxlbGUsIHBhc3RlLCBjb2xsYXBzZSA9ICJcbiIpKQoKc3J0ICU+JSAKICBsZWZ0X2pvaW4oYWxsZWxlX21heF9yYXRpbywgYnkgPSAiY2VsbCIpICU+JSAKICAjIG11dGF0ZShnZW5vdHlwZXIgPSBmY3RfcmVsZXZlbChnZW5vdHlwZXIsICJpbnZpdHJvIikpICU+JSAKICBmaWx0ZXIoIWlzLm5hKG1heF9yYXRpbykpICU+JQogIGdncGxvdChhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBtYXhfcmF0aW8pKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUsIGFscGhhID0gMC41KSsKICAgIGdlb21fbGFiZWwoZGF0YSA9IGFsbGVsZV9sYWJlbCwgYWVzKGNvbG9yID0gTlVMTCwgbGFiZWwgPSBhbGxlbGUsIHggPSAtSW5mLCB5ID0gLUluZiksCiAgICAgICAgICAgIGhqdXN0ID0gLTAuMSwgdmp1c3QgPSAtMC4xLCBzaXplID0gMywgaGp1c3QgPSAwKSArCiAgICB0aGVtZV9idygpICsKICAgIGZhY2V0X2dyaWQoLn5nZW5lKSsKICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50KGhpZ2ggPSAicmVkIiwgbG93ID0gImJsdWUiLCBuYS52YWx1ZSA9ICJ0cmFuc3BhcmVudCIpICsKICAgIGxhYnMoeD0iVU1BUCAxIiwgeT0iVU1BUCAyIiwgY29sb3IgPSAiTWF4aW11bSBBbGxlbGUgXG5GcmVxdWVuY3kiKSsKICAgIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDgsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KICBhbGxlbGVfbWF4X3JhdGlvIDwtIGFsbGVsZV9kYXRhICAlPiUKICAgIHNlbGVjdChnZW5vdHlwZXIsIGNlbGwsIGdlbmUsIGFsbGVsZV9vcmRlciwgYWxsZWxlX3JhdGlvKSAlPiUKICAgICMgZ3JvdXBfYnkoZ2Vub3R5cGVyLCBjZWxsLCBhbGxlbGVfb3JkZXIpICU+JSAgbXV0YXRlKHRlc3QgPSBsZW5ndGgoYWxsZWxlX29yZGVyKSkKICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiYWxsZWxlX29yZGVyIiwgdmFsdWVzX2Zyb20gPSAiYWxsZWxlX3JhdGlvIiwgbmFtZXNfcHJlZml4ID0gImFsbGVsZV8iKSAlPiUKICAgIHJvd3dpc2UoKSAlPiUKICAgIG11dGF0ZShtYXhfcmF0aW8gPSBtYXgoYWNyb3NzKGNvbnRhaW5zKCJhbGxlbGVfIikpLCBuYS5ybSA9IFQpKSAlPiUKICAgIHNlbGVjdChnZW5vdHlwZXIsIGNlbGwsIGdlbmUsIG1heF9yYXRpbykKICAKICBhbGxlbGVfbGFiZWwgPC0gYWxsZWxlX2RhdGEgJT4lIHNlbGVjdChzYW1wbGUsIGdlbm90eXBlciwgZ2VuZSwgYWxsZWxlKSAlPiUgCiAgICBzZXBhcmF0ZShzYW1wbGUsIGludG8gPSBjKCJzYW1wbGUiLCBOVUxMKSwgc2VwID0gIl8iLCBleHRyYSA9ICJkcm9wIikgJT4lIAogICAgZGlzdGluY3QoKSAlPiUgCiAgICBncm91cF9ieShzYW1wbGUsIGdlbm90eXBlciwgZ2VuZSkgJT4lIG5lc3QoKSAlPiUgdW5uZXN0X3dpZGVyKGRhdGEpICU+JSAKICAgIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAKICAgIG11dGF0ZShhbGxlbGUgPSBtYXBfY2hyKGFsbGVsZSwgcGFzdGUsIGNvbGxhcHNlID0gIlxuIikpCiAgCiAgcGx0X21heF9hbGxlbGVfYWxsR2VuZXNUeXBlcnMgPC0gc3J0ICU+JSAKICAgIGxlZnRfam9pbihhbGxlbGVfbWF4X3JhdGlvLCBieSA9IGMoImNlbGwiKSkgJT4lIAogICAgIyBtdXRhdGUoZ2Vub3R5cGVyID0gZmN0X3JlbGV2ZWwoZ2Vub3R5cGVyLCAiaW52aXRybyIpKSAlPiUgCiAgICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgCiAgICBmaWx0ZXIoIWlzLm5hKG1heF9yYXRpbykpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IG1heF9yYXRpbykpICsKICAgICAgcmFzdGVyaXNlKGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjUpLCBkcGkgPSAzMDApKwogICAgICBnZW9tX2xhYmVsKGRhdGEgPSBhbGxlbGVfbGFiZWwsIGFlcyhjb2xvciA9IE5VTEwsIGxhYmVsID0gYWxsZWxlLCB4ID0gLUluZiwgeSA9IC1JbmYpLAogICAgICAgICAgICAgIGhqdXN0ID0gLTAuMSwgdmp1c3QgPSAtMC4xLCBzaXplID0gMywgaGp1c3QgPSAwKSArCiAgICAgIHRoZW1lX2J3KCkgKwogICAgICBmYWNldF9ncmlkKGdlbm90eXBlcn5nZW5lKSsKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnQoaGlnaCA9ICJyZWQiLCBsb3cgPSAiYmx1ZSIsIG5hLnZhbHVlID0gInRyYW5zcGFyZW50IikgKwogICAgICBsYWJzKHg9IlVNQVAgMSIsIHk9IlVNQVAgMiIsIGNvbG9yID0gIk1heGltdW0gQWxsZWxlIFxuRnJlcXVlbmN5IikrCiAgICAgIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkKICAKICBwbHRfbWF4X2FsbGVsZV9hbGxHZW5lc1R5cGVycwpgYGAKCiMgTWV0YS1hbmFseXNpcyBhcHByb2FjaAoKIyMjIFJhdGlvbmFsZSAKCi0gR29hbDogZGV0ZXJtaW5lIGEgc3VtbWFyeSBzdGF0aXN0aWMgZm9yIGFsbGVsZSByYXRpbyBhY3Jvc3MgYWxsIGNlbGxzCi0gUHJvYmxlbToKICAtIEVhY2ggY2VsbCBpbiBzY1JOQSBleHBlcmltZW50IGhhcyBpdHMgb3duIHJhdGlvIG9mIEhMQSBhbGxlbGUgMSByZWFkcyA6IEhMQSBhbGxlbGUgMiByZWFkcwogIC0gQSB3aWRlIHJhbmdlIG9mIHRvdGFsIHJlYWQgY291bnRzIG1heSBnbyBpbnRvIGVhY2ggY2VsbCdzIHJhdGlvCiAgLSBTbWFsbCBkaWZmZXJlbmNlIGluIHJlYWQgY291bnRzIGZvciBjZWxscyB3aXRoIGxvdyB0b3RhbCByZWFkcyBjYW4gZ3JlYXRseSBhbHRlciB0aGUgYWxsZWxlIHJhdGlvIGNvbXBhcmVkIHRvIGNlbGxzIHdpdGggaGlnaCB0b3RhbCByZWFkIGNvdW50cwogICAgLSBJLmUuIENlbGxzIHdpdGggbG93IHJlYWQgY291bnRzIGFyZSBtb3JlIGxpa2VseSB0byBoYXZlIGltcHJlY2lzZSBhbGxlbGUgcmF0aW9zCiAgLSBTaW1wbHkgYXZlcmFnaW5nIHJhdGlvcyB3b3VsZCBlcXVhbGx5IHdlaWdodCBoaWdoIGNvbmZpZGVuY2UsIGhpZ2ggcmVhZCBjb3VudCByYXRpb3Mgd2l0aCBsb3cgY29uZmlkZW5jZSwgbG93IHJlYWQgcmF0aW9zCi0gQXBwcm9hY2gKICAtIE1ldGEtYW5hbHlzaXMgbWV0aG9kcyBzcGVjaWZpY2FsbHkgYWRkcmVzcyB0eXBlIG9mIHByb2JsZW0sIHR5cGljYWxseSBieSB3ZWlnaGluZyBhbiBlZmZlY3QgYnkgdGhlIGludmVyc2Ugb2YgaXRzIHZhcmlhbmNlCiAgLSBJbiB0aGUgc2V0dGluZyBvZiByYXRpb3MsIGJhc2ljIGFwcHJvYWNoIHdvdWxkIGJlIHRvIGNvbWJpbmUgbG9nLW9kZHMgcmF0aW8gb2YgYWxsZWxlcywgd2VpZ2h0ZWQgYnkgaW52ZXJzZSB2YXJpYW5jZQogIC0gV291bGQgdXNlIGEgcmFuZG9tIGVmZmVjdHMgbW9kZWwgYmVjYXVzZSBkbyBub3QgZXhwZWN0IGVhY2ggY2VsbCBpbiBhIGdyb3VwIHdvdWxkIGhhdmUgdGhlIHNhbWUgZXhhY3QgcmF0aW8gZHVlIHRvIHZhcmlvdXMgc3RvY2hhc3RpYyB0cmFuc2NyaXB0aW9uYWwgZWZmZWN0cwoKIyMjIENhbGN1bGF0aW5nIHN1bW1hcnkgZWZmZWN0IHNpemVzCgotIEFuYWx5c2lzIGNhbiB0YWtlIHNvbWUgdGltZSwgc28gcnVuIG9uIGFuIEhQQyBjbHVzdGVyIHVzaW5nIHNsdXJtCi0gQ29udGFpbmVkIGluIGEgc2VwYXJhdGUgc2NyaXB0IGBzY19tZXRhLlJgOgoKIyMjIyBDb250ZW50cyBvZiBgc2NfbWV0YS5SYAoKYGBge3IsIGV2YWwgPSBGfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShtZXRhKQoKc291cmNlKGhlcmUoImhlbHBlcl9mdW5jdGlvbnMvZGF0YV9pbXBvcnRfZnVuY3Rpb25zLlIiKSkKaXNiX3BhdGggPC0gaGVyZSgiZGF0YS9pc2IiKQoKc3J0IDwtIHJlYWRSRFMoaGVyZSgiZGF0YS9pc2IvaXNiX3NjX21ldGFkYXRhLlJEUyIpKQpjZWxscyA8LSBjKCJDRDE0IE1vbm9jeXRlIiwgIkNENCBUIiwgIkNEOCBUIiwgIk5LIiwgIkIiLCAiQ0QxNiBNb25vY3l0ZSIsICJjREMiLCAicERDIikKc3J0IDwtIHNydCAlPiUgc2VsZWN0KHNhbXBsZSwgc2FtcGxlSUQsIGNlbGwsIGNlbGx0eXBlLCBzZXZlcml0eSwgVU1BUF8xLCBVTUFQXzIpICU+JSAKICBmaWx0ZXIoY2VsbHR5cGUgJWluJSBjZWxscykKCiMgRnVuY3Rpb24gdG8gcGVyZm9ybSBtZXRhLWFuYWx5c2lzIG9uIGRhdGFmcmFtZSB3aGVyZQojIGVhY2ggcm93IGlzIGEgY2VsbCBhbmQgY29sdW1uczoKIyBgb2JzZXJ2ZWRgIChyZWFkcyBvZiBkb21pbmFudCBhbGxlbGUpCiMgYGdlbmVfc3VtX3R5cGVkYCAodG90YWwgY2VsbCByZWFkcykKIyBgZXhwZWN0ZWRgIChyZWFkcyBvZiBvbmUgYWxsZWxlIGV4cGVjdGVkIGlmIDUwOjUwIGFsbGVsZV8xIDogYWxsZWxlXzIpCnNjX21ldGEgPC0gZnVuY3Rpb24oZGYpewogIGwgPC0gbnJvdyhkZikKICBtIDwtIG1ldGFiaW4oZXZlbnQuZSA9IG9ic2VydmVkLCBuLmUgPSBnZW5lX3N1bV90eXBlZCwgZXZlbnQuYyA9IGV4cGVjdGVkLCBuLmMgPSBnZW5lX3N1bV90eXBlZCwKICAgICAgICAgICAgICAgZGF0YSA9IGRmLAogICAgICAgICAgICAgICBtZXRob2QgPSAiSW52ZXJzZSIsCiAgICAgICAgICAgICAgIGluY3IgPSAwLjEsCiAgICAgICAgICAgICAgIHNtID0gIk9SIikKICBkYXRhLmZyYW1lKHN1bW1hcnkobSkkcmFuZG9tKSAlPiUgCiAgICBtdXRhdGUobl9jZWxscyA9IGwpICU+JSAKICAgIHNlbGVjdChURSwgc2VURSwgbG93ZXIsIHVwcGVyLCBuX2NlbGxzKSAKfQoKIyBJbXBvcnQgZGF0YSBiYXNlZCBvbiBzYW1wbGUgYW5kIGdlbm90eXBlcgpjZWxsX3N0YXRzIDwtIGV4cGFuZF9ncmlkKAogIGdlbm90eXBlciA9IGMoImludml0cm8iLCAiYXJjYXNITEEiLCAib3B0aXR5cGUiLCAicGhsYXQiLCAiaGxhbWluZXIiKSwKICBzYW1wbGUgPSByZWFkX2xpbmVzKGhlcmUoImRhdGEvaXNiL3NjSExBY291bnQvQkxfZmFzdHFfZmlsZXMudHh0IikpKSAlPiUgCiAgIyBoZWFkKDUpICU+JSAjIFNwZWNpZnkgbGltaXQgdG8gbnVtYmVyIG9mIG1ldGEtYW5hbHlzZXMKICBtdXRhdGUoZGF0YSA9IG1hcDIoc2FtcGxlLCBnZW5vdHlwZXIsIGZ1bmN0aW9uKHMsZyl7CiAgICByZXN1bHRfcGF0aCA9IHNwcmludGYoIiVzL3NjSExBY291bnQvb3V0cHV0X2FzZS8lcyIsaXNiX3BhdGgsIGcpCiAgICBiYXJjb2RlX3BhdGggPSBzcHJpbnRmKCIlcy9zY0hMQWNvdW50L2JhcmNvZGVzIiwgaXNiX3BhdGgpCiAgICBzY0hMQV9kYXRhX3Byb2Nlc3Npbmcoc2FtcGxlID0gcywKICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRfZGlyID0gcmVzdWx0X3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmFyY29kZV9kaXIgPSBiYXJjb2RlX3BhdGgpCiAgfSkpICU+JSB1bm5lc3QoZGF0YSkgJT4lIAogIGZpbHRlcighaXMubmEoY2VsbCkpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gZ3N1YigiX1tBLVpdWzAtOV0kIiwiIixzYW1wbGUpKSAlPiUgIyBDb25zb2xpZGF0ZSBzYW1wbGVzCiAgbGVmdF9qb2luKHNydCAlPiUgc2VsZWN0KGNlbGx0eXBlLCBjZWxsKSwgYnkgPSAiY2VsbCIpICU+JSAjIEFkZCBjZWxsdHlwZXMKICBmaWx0ZXIoY2VsbHR5cGUgJWluJSBjZWxscykgIyBLZWVwIG9ubHkgc3RhbmRhcmQgY2VsbCB0eXBlcwoKbWV0YV9kZiA8LSBjZWxsX3N0YXRzICU+JSAKICAjIEtlZXAgb25seSBtb3N0IGV4cHJlc3NlZCBhbGxlbGUgKG9yIHJhbmRvbSBpZiA1MDo1MCkKICBncm91cF9ieShzYW1wbGUsIGdlbm90eXBlciwgZ2VuZSwgY2VsbCkgJT4lIAogIHNsaWNlX21heChvcmRlcl9ieSA9IGFsbGVsZV9yYXRpbywgbiA9IDEsIHdpdGhfdGllcyA9IEYpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogICMgRmlsbCBvdXQgY29udGluZ2VuY3kgdGFibGUKICBtdXRhdGUoY29tcGxlbWVudCA9IGdlbmVfc3VtX3R5cGVkIC0gY291bnQsIAogICAgICAgICBleHBlY3RlZCA9IDAuNSpnZW5lX3N1bV90eXBlZCkgJT4lIAogIHJlbmFtZShvYnNlcnZlZCA9IGNvdW50KSAlPiUgCiAgc2VsZWN0KHNhbXBsZSwgZ2Vub3R5cGVyLCBjZWxsdHlwZSwgZ2VuZSwgb2JzZXJ2ZWQsIGV4cGVjdGVkLCBnZW5lX3N1bV90eXBlZCkgJT4lIAogIGdyb3VwX2J5KHNhbXBsZSwgZ2Vub3R5cGVyLCBjZWxsdHlwZSwgZ2VuZSkgJT4lCiAgIyBOZXN0IGFuZCBydW4gbWV0YS1hbmFseXNpcwogIG5lc3QoKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShkYXRhID0gbWFwKGRhdGEsZnVuY3Rpb24oeCkge3NjX21ldGEoeCl9KSkgJT4lCiAgdW5uZXN0KGRhdGEpCgp3cml0ZV9jc3YobWV0YV9kZiwgaGVyZSgiN19ITEFfQVNFL21ldGFfYW5hbHlzaXNfcmVzdWx0c18xMi5jc3YiKSkKYGBgCgojIyMgU2luZ2xlIHNhbXBsZSBhbmFseXNpcwoKYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQptZXRhX2RmIDwtIHJlYWRfY3N2KGhlcmUoIjdfSExBX0FTRS9tZXRhX2FuYWx5c2lzX3Jlc3VsdHNfYzEyLmNzdiIpKSAKbWV0YV9kZgpgYGAKYGBge3IsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CnBsb3RfbWV0YV9vZGRzIDwtIGZ1bmN0aW9uKGRmLCBjZWxsX3R5cGUsIGxvY3VzKXsKICBkZiAlPiUgCiAgZmlsdGVyKGNlbGx0eXBlID09IGNlbGxfdHlwZSwgZ2VuZSA9PSBsb2N1cykgJT4lIAogIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JQogIGdncGxvdChhZXMoeCA9IGdlbm90eXBlciwgeSA9IFRFLCB5bWluID0gbG93ZXIsIHltYXggPSB1cHBlcikpKwogIGdlb21fcG9pbnQoKSsKICBnZW9tX2Vycm9yYmFyKHdpZHRoID0gMC40KSsKICB0aGVtZV9idygpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsTkEpKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgbGFicyh5ID0gIkxvZy1PZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwpCn0KCnBsdF9tZXRhIDwtIG1ldGFfZGYgJT4lIAogIGZpbHRlcihzYW1wbGUgPT0gc2FtcCkgJT4lIAogIHBsb3RfbWV0YV9vZGRzKGNlbGxfdHlwZSA9ICJjREMiLCBsb2N1cyA9ICJBIikKCnBsdF9tZXRhCmBgYApgYGB7ciwgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9M30KbWV0YV9kZiAlPiUgCiAgZmlsdGVyKHNhbXBsZSA9PSBzYW1wKSAlPiUKICBwbG90X21ldGFfb2RkcyhjZWxsX3R5cGUgPSAiY0RDIiwgbG9jdXMgPSAiRFJCMSIpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zfQpzYW1wbGVzIDwtIHVuaXF1ZShtZXRhX2RmJHNhbXBsZSkKZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZXMpKXsKcGx0X21ldGEgPC0gbWV0YV9kZiAlPiUgCiAgZmlsdGVyKHNhbXBsZSA9PSBzYW1wbGVzW2ldKSAlPiUgCiAgcGxvdF9tZXRhX29kZHMoY2VsbF90eXBlID0gIkNEMTQgTW9ub2N5dGUiLCBsb2N1cyA9ICJBIikgKwogIGdndGl0bGUoc2FtcGxlc1tpXSkKICBwcmludChwbHRfbWV0YSkKfQpgYGAKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpwbHRfbWV0YV9hbGxMb2NpIDwtIG1ldGFfZGYgJT4lIAogIGZpbHRlcihzYW1wbGUgPT0gc2FtcCkgJT4lCiAgZmlsdGVyKGNlbGx0eXBlID09ICJjREMiKSAlPiUgCiAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZ2Vub3R5cGVyLCB5ID0gVEUsIHltaW4gPSBsb3dlciwgeW1heCA9IHVwcGVyKSkrCiAgZ2VvbV9wb2ludCgpKwogIGdlb21fZXJyb3JiYXIod2lkdGggPSAwLjQpKwogIGZhY2V0X2dyaWQoLn5nZW5lKSsKICB0aGVtZV9idygpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsTkEpKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgbGFicyh5ID0gIkxvZy1PZGRzIHJhdGlvXG5vZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMKQoKcGx0X21ldGFfYWxsTG9jaSAgCmBgYAoKCgojIyMgTXVsdGktc2FtcGxlIGFuYWx5c2lzCgpgYGB7ciwgbWVzc2FnZSA9IEZ9Cm1ldGFfZGYgPC0gcmVhZF9jc3YoaGVyZSgiN19ITEFfQVNFL21ldGFfYW5hbHlzaXNfcmVzdWx0c19jMTIuY3N2IikpICU+JSAKICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpLAogICAgICAgICBjZWxsdHlwZSA9IGZhY3RvcihjZWxsdHlwZSwgbGV2ZWxzID0gYygKICAgICAgICAgICJjREMiLCAiQ0QxNCBNb25vY3l0ZSIsICJCIiwgInBEQyIsICJDRDE2IE1vbm9jeXRlIiwgIkNEOCBUIiwgIkNENCBUIiwgIk5LIikpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDYsIHdhcm5pbmcgPSBGfQptZXRhX2RmICU+JSAKICBnZ3Bsb3QoYWVzKHg9Y2VsbHR5cGUseT1URSwgZmlsbCA9IGNlbGx0eXBlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMiwgc2l6ZSA9IDAuMikrCiAgc3RhdF9zdW1tYXJ5KGZ1bj1tZWFuLCBzdGF0PSAicG9pbnQiKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJlcnJvcmJhciIpKwogIGZhY2V0X2dyaWQoZ2VuZX5nZW5vdHlwZXIpKwogIHRoZW1lX2J3KCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogIGxhYnMoeSA9ICJMb2ctT2RkcyByYXRpbyBvZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMLCBmaWxsID0gIkNlbGwgdHlwZSIpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikKCm1ldGFfZGYgJT4lIAogIGdncGxvdChhZXMoeD1nZW5vdHlwZXIseT1URSwgZmlsbCA9IGNlbGx0eXBlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMiwgc2l6ZSA9IDAuMikrCiAgc3RhdF9zdW1tYXJ5KGZ1bj1tZWFuLCBzdGF0PSAicG9pbnQiKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJlcnJvcmJhciIpKwogIGZhY2V0X2dyaWQoZ2VuZX5jZWxsdHlwZSkrCiAgdGhlbWVfYncoKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgbGFicyh5ID0gIkxvZy1PZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwsIGZpbGwgPSAiQ2VsbCB0eXBlIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKQpgYGAKCmBgYHtyfQogbWV0YV9kZiAlPiUgCiAgZmlsdGVyKHNhbXBsZSA9PSBzYW1wKSAlPiUKICBmaWx0ZXIoY2VsbHR5cGUgPT0gImNEQyIpICU+JSAKICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBnZW5vdHlwZXIsIHkgPSBURSwgeW1pbiA9IGxvd2VyLCB5bWF4ID0gdXBwZXIpKSsKICBnZW9tX3BvaW50KCkrCiAgZ2VvbV9lcnJvcmJhcih3aWR0aCA9IDAuNCkrCiAgZmFjZXRfZ3JpZCgufmdlbmUpKwogIHRoZW1lX2J3KCkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCxOQSkpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsKICBsYWJzKHkgPSAiTG9nLU9kZHMgcmF0aW9cbm9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwpCmBgYAoKCiMjIyBDb3JyZWxhdGlvbiBhbmFseXNpcwoKYGBge3IsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nPUZ9Cm1ldGFfZGYgJT4lIAogIGZpbHRlcihnZW5lID09ICJBIiwgY2VsbHR5cGUgPT0gImNEQyIpICU+JSAKICBzZWxlY3Qoc2FtcGxlLCBnZW5vdHlwZXIsIFRFKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJnZW5vdHlwZXIiLCB2YWx1ZXNfZnJvbSA9ICJURSIpICU+JSAKICBzZWxlY3QoLXNhbXBsZSkgJT4lIAogIEdHYWxseTo6Z2dwYWlycyhwcm9ncmVzcyA9IEZBTFNFKSArCiAgdGhlbWVfYncoKQpgYGAKCmBgYHtyLCBtZXNzYWdlID0gRiwgd2FybmluZz1GfQphY2N1cmFjeV9kZiA8LSByZWFkUkRTKGhlcmUoIjNfRFJCL2lzYl9hY2N1cmFjeV9kcmIzNDVfZmlsdGVyZWQuUkRTIikpICU+JSAKICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgCiAgc2VsZWN0KHNhbXBsZSwgZ2VuZT1sb2N1cywgZ2Vub3R5cGVyLCBhY2N1cmFjeSkgJT4lIAogIGRpc3RpbmN0KCkKZm9yIChpIGluIGMoMCwxKSl7CiAgc3VwcHJlc3NXYXJuaW5ncyh7CiAgcGx0IDwtIG1ldGFfZGYgJT4lIAogICAgdW5ncm91cCgpICU+JSAKICAgIGZpbHRlcihnZW5lID09ICJBIiwgY2VsbHR5cGUgPT0gImNEQyIpICU+JSAKICAgIGxlZnRfam9pbihhY2N1cmFjeV9kZiwgYnkgPSBjKCJzYW1wbGUiLCAiZ2VuZSIsICJnZW5vdHlwZXIiKSkgJT4lIAogICAgZmlsdGVyKGFjY3VyYWN5ID09IGkgfCBnZW5vdHlwZXIgPT0gIkdyb3VuZCB0cnV0aCIpICU+JSAKICAgICMgZHJvcF9uYShhY2N1cmFjeSkgJT4lIAogICAgc2VsZWN0KHNhbXBsZSwgZ2Vub3R5cGVyLCBURSkgJT4lIAogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJnZW5vdHlwZXIiLCB2YWx1ZXNfZnJvbSA9ICJURSIpICU+JSAKICAgIHNlbGVjdCgtc2FtcGxlKSAlPiUgCiAgICBHR2FsbHk6OmdncGFpcnMocHJvZ3Jlc3MgPSBGQUxTRSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3RpdGxlKHNwcmludGYoIkNvcnJlbGF0aW9uIG9mIGFsbGVsZSByYXRpb3Mgd2hlcmUgYWNjdXJhY3kgPSAlcyIsIGkpKQogIHByaW50KHBsdCkKICB9KQp9CmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGfQpjb3JyX2RmIDwtIG1ldGFfZGYgJT4lIAogIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAKICBzZWxlY3Qoc2FtcGxlLCBnZW5vdHlwZXIsIGNlbGx0eXBlLCBnZW5lLCBURSkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiZ2Vub3R5cGVyIiwgdmFsdWVzX2Zyb20gPSAiVEUiKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoYXJjYXNITEEsIEhMQW1pbmVyLCBPcHRpVHlwZSwgUEhMQVQpLCBuYW1lc190byA9ICJnZW5vdHlwZXIiLCB2YWx1ZXNfdG8gPSAiVEUiKSAlPiUgCiAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkKY29ycl9kZiAlPiUgCiAgZ2dwbG90KGFlcyh4PWBHcm91bmQgdHJ1dGhgLCB5PVRFKSkrCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSsKICBmYWNldF9ncmlkKGdlbmUgfiBnZW5vdHlwZXIpICsKICB0aGVtZV9idygpICsKICBnZ3B1YnI6OnN0YXRfY29yKGFlcyhsYWJlbCA9IC4ucnIubGFiZWwuLiksIGxhYmVsLngubnBjID0gImxlZnQiLCBsYWJlbC55Lm5wYyA9ICJ0b3AiLCBnZW9tID0gImxhYmVsIikKYGBgCgpgYGB7cn0KZm9yIChpIGluIGMoMCwxKSl7CiAgc3VwcHJlc3NXYXJuaW5ncyh7CiAgcGx0IDwtIGNvcnJfZGYgJT4lIAogICAgdW5ncm91cCgpICU+JSAKICAgICMgZmlsdGVyKGdlbmUgPT0gIkEiLCBjZWxsdHlwZSA9PSAiY0RDIikgJT4lIAogICAgbGVmdF9qb2luKGFjY3VyYWN5X2RmLCBieSA9IGMoInNhbXBsZSIsICJnZW5lIiwgImdlbm90eXBlciIpKSAlPiUgCiAgICBmaWx0ZXIoYWNjdXJhY3kgPT0gaSkgJT4lIAogICAgZ2dwbG90KGFlcyh4PWBHcm91bmQgdHJ1dGhgLCB5PVRFKSkrCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpKwogICAgZmFjZXRfZ3JpZChnZW5lIH4gZ2Vub3R5cGVyKSArCiAgICB0aGVtZV9idygpICsKICAgIGdncHVicjo6c3RhdF9jb3IoYWVzKGxhYmVsID0gLi5yci5sYWJlbC4uKSwgbGFiZWwueC5ucGMgPSAibGVmdCIsIGxhYmVsLnkubnBjID0gInRvcCIsIGdlb20gPSAibGFiZWwiKSArCiAgICAgIGdndGl0bGUoc3ByaW50ZigiQ29ycmVsYXRpb24gb2YgYWxsZWxlIHJhdGlvcyB3aGVyZSBhY2N1cmFjeSA9ICVzIiwgaSkpICsKICAgIGxhYnMoeSA9ICJQcmVkaWN0ZWQgZ2Vub3R5cGUgSExBIGFsbGVsZSBsb2ctb2RkcyByYXRpbyIsIHggPSAiR3JvdW5kIHRydXRoIGdlbm90eXBlIEhMQSBhbGxlbGUgbG9nLW9kZHMgcmF0aW8iKQogIGFzc2lnbihzcHJpbnRmKCJwbHRfbWV0YV9jb3JyXyVzIiwgaSksIHBsdCkKICBwcmludChwbHQpCiAgfSkKfQoKYGBgCmBgYHtyfQpwbHRfbWV0YV9jb3JyX2FiYnJldiA8LSBjb3JyX2RmICU+JSAKICBsZWZ0X2pvaW4oYWNjdXJhY3lfZGYsIGJ5ID0gYygic2FtcGxlIiwgImdlbmUiLCAiZ2Vub3R5cGVyIikpICU+JSAKICBmaWx0ZXIoZ2VuZSA9PSAiQSIsIGFjY3VyYWN5ICVpbiUgYygwLDAuNSwxKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWBHcm91bmQgdHJ1dGhgLCB5PVRFKSkrCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpKwogICAgZmFjZXRfZ3JpZChhY2N1cmFjeSB+IGdlbm90eXBlciwgbGFiZWxsZXIgPSBsYWJlbGxlcihhY2N1cmFjeSA9IGZ1bmN0aW9uKHgpIHNwcmludGYoIkFjY3VyYWN5OiAlcyIsIHgpKSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3B1YnI6OnN0YXRfY29yKGFlcyhsYWJlbCA9IC4ucnIubGFiZWwuLiksIGxhYmVsLngubnBjID0gImxlZnQiLCBsYWJlbC55Lm5wYyA9ICJ0b3AiLCBnZW9tID0gImxhYmVsIiwgbWV0aG9kID0gInBlYXJzb24iKSArCiAgICBsYWJzKHkgPSAiTG9nLW9kZHMgcmF0aW8gdXNpbmcgcHJlZGljdGVkIGdlbm90eXBlIiwgeCA9ICJMb2ctb2RkcyByYXRpbyB1c2luZyBncm91bmQgdHJ1dGggZ2Vub3R5cGVyIikKcGx0X21ldGFfY29ycl9hYmJyZXYKYGBgCmBgYHtyfQphY2N1cmFjeV9kZiAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBkcm9wX25hKCkgJT4lIAogIGNvdW50KGdlbmUsIGdlbm90eXBlciwgYWNjdXJhY3kpCmBgYAoKCgojIyMgQnkgc2V2ZXJpdHkKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDYsIHdhcm5pbmcgPSBGfQptZXRhX2RmICU+JSAKICBmaWx0ZXIoZ2Vub3R5cGVyID09ICJHcm91bmQgdHJ1dGgiKSAlPiUgCiAgbGVmdF9qb2luKAogICAgc3J0ICU+JSAKICAgICAgc2VsZWN0KHNhbXBsZSA9IHNhbXBsZUlELCBzZXZlcml0eSkgJT4lIAogICAgICBkaXN0aW5jdCgpLAogICAgYnkgPSAic2FtcGxlIgogICkgJT4lIAogIGdncGxvdChhZXMoeD1zZXZlcml0eSx5PVRFLCBmaWxsID0gY2VsbHR5cGUpKSsKICAgIGdlb21fdmlvbGluKCkrCiAgICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMiwgc2l6ZSA9IDAuMikrCiAgICBzdGF0X3N1bW1hcnkoZnVuPW1lYW4sIHN0YXQ9ICJwb2ludCIpKwogICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgZ2VvbT0iZXJyb3JiYXIiKSsKICAgIGZhY2V0X2dyaWQoZ2VuZX5jZWxsdHlwZSkrCiAgICB0aGVtZV9idygpKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogICAgbGFicyh5ID0gIkxvZy1PZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwsIGZpbGwgPSAiQ2VsbCB0eXBlIikgKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgd2FybmluZyA9IEZ9Cm1ldGFfZGYgJT4lIAogIGZpbHRlcihnZW5vdHlwZXIgPT0gIkdyb3VuZCB0cnV0aCIpICU+JSAKICBsZWZ0X2pvaW4oCiAgICBzcnQgJT4lIAogICAgICBzZWxlY3Qoc2FtcGxlID0gc2FtcGxlSUQsIHNldmVyaXR5KSAlPiUgCiAgICAgIGRpc3RpbmN0KCksCiAgICBieSA9ICJzYW1wbGUiCiAgKSAlPiUgCiAgbXV0YXRlKHNldmVyaXR5ID0gYXMubnVtZXJpYyhzZXZlcml0eSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBzZXZlcml0eSwgeSA9IFRFKSkgKwogICMgZ2VvbV9wb2ludCgpICsgCiAgc3RhdF9zdW1tYXJ5KGZ1bj1tZWFuLCBzdGF0PSAicG9pbnQiLCBzaXplID0gMC4yKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJlcnJvcmJhciIsIHNpemUgPSAwLjIpKwogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIpKwogIGZhY2V0X2dyaWQoY2VsbHR5cGV+Z2VuZSkrCiAgdGhlbWVfYncoKSAKYGBgCgojIFBsb3RzIGZvciBmaWd1cmVzCgojIEFzc2VtYmxlIHBsb3QKYGBge3IsIGZpZy53aWR0aCA9IDE0LCBmaWcuaGVpZ2h0ID0gMTB9CiMgcGx0X2FsbGVsZV9mcmVxX2xnZCA8LSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9hbGxlbGVfZnJlcSkKIyBwbHRfbWF4X2FsbGVsZV9sZ2QgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfbWF4X2FsbGVsZSkKY29sXzEgPC0gcGxvdF9ncmlkKAogIHBsdF9hbGxlbGVfZnJlcSwKICBwbHRfbWF4X2FsbGVsZSwKICBuY29sID0gMSwKICByZWxfaGVpZ2h0cyA9IGMoNSwzKSwKICBhbGlnbiA9ICJ2IiwgYXhpcyA9ICJsciIsCiAgbGFiZWxzID0gTEVUVEVSU1sxOjJdCikKCmNvbF8yIDwtIHBsb3RfZ3JpZCgKICBwbHRfc3J0LAogIHBsdF9tZXRhLAogIG5jb2wgPSAxLAogIGFsaWduID0gInYiLCBheGlzID0gImxyIiwKICBsYWJlbHMgPSBMRVRURVJTWzM6NF0sCiAgaGp1c3QgPSAwLjUKKQpyb3dfMSA8LSBwbG90X2dyaWQoCiAgY29sXzEsCiAgY29sXzIsCiAgbnJvdyA9IDEsCiAgcmVsX3dpZHRocyA9IGMoNiwzKQopCnJvd18yIDwtIHBsb3RfZ3JpZCgKICBwbHRfbWV0YV9jb3JyXzAgK2dndGl0bGUoTlVMTCksCiAgcGx0X21ldGFfY29ycl8xICtnZ3RpdGxlKE5VTEwpLAogIGxhYmVscyA9IExFVFRFUlNbNTo2XSwKICBuY29sID0gMgopCnBsb3RfZ3JpZCgKICByb3dfMSwKICByb3dfMiwKICByZWxfaGVpZ2h0cyA9IGMoMywyKSwKICBuY29sID0gMQopCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDE0LCBmaWcuaGVpZ2h0ID0gNn0KY29sXzEgPC0gcGxvdF9ncmlkKAogIHBsdF9zcnQsCiAgcGx0X21ldGEsCiAgbmNvbCA9IDEsCiAgYWxpZ24gPSAidiIsIGF4aXMgPSAibHIiLAogIGxhYmVscyA9IExFVFRFUlNbYygxLDMpXSwKICBsYWJlbF94ID0gLTAuMDUKKQoKY29sXzIgPC0gcGxvdF9ncmlkKAogIHBsdF9tYXhfYWxsZWxlLAogIHBsdF9tZXRhX2NvcnJfYWJicmV2LAogIG5jb2wgPSAxLAogIHJlbF9oZWlnaHRzID0gYygzLDUpLAogIGFsaWduID0gInYiLCBheGlzID0gImxyIiwKICBsYWJlbHMgPSBMRVRURVJTW2MoMiw0KV0sCiAgbGFiZWxfeCA9IC0wLjA1CikKCnBsdF9maWdfbWFpbiA8LSBwbG90X2dyaWQoCiAgTlVMTCwKICBjb2xfMSwKICBjb2xfMiwKICBucm93ID0gMSwKICByZWxfd2lkdGhzID0gYygwLjI1LDMsNikKKQoKcGx0X2ZpZ19tYWluCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDE0LCBmaWcuaGVpZ2h0ID0gNX0KY29sXzEgPC0gcGxvdF9ncmlkKAogIHBsdF9zcnQsCiAgcGx0X21ldGErIGxhYnMoeT0iTG9nLW9kZHMgcmF0aW9cbm9mIGRvbWluYW50IGFsbGVsZSIpLAogIG5jb2wgPSAxLAogIGFsaWduID0gInYiLCBheGlzID0gImxyIiwKICBsYWJlbHMgPSBMRVRURVJTW2MoMSwzKV0sCiAgbGFiZWxfeCA9IC0wLjA1CikKCmNvbF8yIDwtIHBsb3RfZ3JpZCgKICBwbHRfbWF4X2FsbGVsZSwKICBwbHRfbWV0YV9jb3JyX2FiYnJldiArIGZhY2V0X2dyaWQoLiB+IGdlbm90eXBlcikgKyBsYWJzKHk9IkxvZy1vZGRzIHJhdGlvIHVzaW5nXG5wcmVkaWN0ZWQgZ2Vub3R5cGUiKSwKICBuY29sID0gMSwKICByZWxfaGVpZ2h0cyA9IGMoMSwxKSwKICBhbGlnbiA9ICJ2IiwgYXhpcyA9ICJsciIsCiAgbGFiZWxzID0gTEVUVEVSU1tjKDIsNCldLAogIGxhYmVsX3ggPSAtMC4wNQopCgpwbHRfZmlnX21haW4gPC0gcGxvdF9ncmlkKAogIE5VTEwsCiAgY29sXzEsCiAgTlVMTCwKICBjb2xfMiwKICBucm93ID0gMSwKICByZWxfd2lkdGhzID0gYygwLjI1LDMsMC4yNSw2KQopCgpwbHRfZmlnX21haW4KYGBgCgpgYGB7cn0Kc2F2ZV9wbG90KGhlcmUoImZpZ3VyZXNfcGRmL3YyLzdfc2NITEEucGRmIiksIHBsdF9maWdfbWFpbiwgYmFzZV9oZWlnaHQgPSA1LCBiYXNlX3dpZHRoID0gMTQpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQ9NX0KcGx0X2ZpZ19zdXBwIDwtIHBsdF9tZXRhX2NvcnJfYWJicmV2ICsgZmFjZXRfZ3JpZChhY2N1cmFjeSB+IGdlbm90eXBlciwgbWFyZ2lucyA9ICJhY2N1cmFjeSIpIApwbHRfZmlnX3N1cHAKYGBgCmBgYHtyfQpzYXZlX3Bsb3QoaGVyZSgiZmlndXJlc19wZGYvdjIvczhfQVNFX2NvcnJlbGF0aW9uLnBkZiIpLCBwbHRfZmlnX3N1cHAsIGJhc2VfaGVpZ2h0ID0gNSwgYmFzZV93aWR0aCA9IDYpCmBgYApgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEyfQpwbHRfZmlnX3N1cHBfYWxsTG9jaSA8LSBwbG90X2dyaWQoCiAgcGx0X21heF9hbGxlbGVfYWxsR2VuZXNUeXBlcnMsCiAgcGx0X21ldGFfYWxsTG9jaSwKICBuY29sID0gMSwKICByZWxfaGVpZ2h0cyA9IGMoMC44LDAuMiksIAogIGFsaWduID0gInYiLAogIGF4aXMgPSAibHIiLAogIGxhYmVscyA9IExFVFRFUlNbMToyXQopCgpwbHRfZmlnX3N1cHBfYWxsTG9jaQpgYGAKYGBge3J9CnNhdmVfcGxvdChoZXJlKCJmaWd1cmVzX3BkZi92Mi9zN19BU0VfYWxsTG9jaS5wZGYiKSwgcGx0X2ZpZ19zdXBwX2FsbExvY2ksIGJhc2VfaGVpZ2h0ID0gMTAsIGJhc2Vfd2lkdGggPSAxMikKYGBgCgojIFNhdmUgYWxsIGZpZ3VyZSBvYmplY3RzIApgYGB7cn0KbGlzdE4gPC0gZnVuY3Rpb24oLi4uKSB7CiAgYW5vbkxpc3QgPC0gbGlzdCguLi4pCiAgbmFtZXMoYW5vbkxpc3QpIDwtIGFzLmNoYXJhY3RlcihzdWJzdGl0dXRlKGxpc3QoLi4uKSkpWy0xXQogIGFub25MaXN0Cn0KCnNhdmVSRFMoCiAgbGlzdE4ocGx0X3NydCwKICAgICAgICBwbHRfbWV0YSwKICAgICAgICBwbHRfbWF4X2FsbGVsZSwKICAgICAgICBwbHRfbWV0YV9jb3JyX2FiYnJldgogICAgICAgICksCiAgaGVyZSgiZmlndXJlc19vYmplY3QvN19zY0hMQV9vYmplY3RzLlJEUyIpCikKYGBgCgojIE9sZCB3b3JrCgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBzY19tZXRhIDwtIGZ1bmN0aW9uKGRmKXsgLS0+CjwhLS0gICBtIDwtIG1ldGFiaW4oZXZlbnQuZSA9IG9ic2VydmVkLCBuLmUgPSBnZW5lX3N1bV90eXBlZCwgZXZlbnQuYyA9IGV4cGVjdGVkLCBuLmMgPSBnZW5lX3N1bV90eXBlZCwgLS0+CjwhLS0gICAgICAgICBkYXRhID0gZGYsIC0tPgo8IS0tICAgICAgICAgbWV0aG9kID0gIkludmVyc2UiLCAtLT4KPCEtLSAgICAgICAgIGluY3IgPSAwLjEsIC0tPgo8IS0tICAgICAgICAgc20gPSAiT1IiKSAtLT4KPCEtLSAgIGRhdGEuZnJhbWUoc3VtbWFyeShtKSRyYW5kb20pICU+JSAgLS0+CjwhLS0gICAgIHNlbGVjdChURSwgc2VURSwgbG93ZXIsIHVwcGVyKSAlPiUgIC0tPgo8IS0tICAgICBtdXRhdGVfYWxsKGV4cCkgLS0+CjwhLS0gfSAtLT4KPCEtLSBgYGAgLS0+CgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gc2FtcCA8LSAiSU5DT1YxWzAtMl1bMC05XS1CTCIgLS0+CjwhLS0geCA8LSB0aWJibGUoZ2Vub3R5cGVyID0gYygiaW52aXRybyIsICJhcmNhc0hMQSIsICJvcHRpdHlwZSIsICJwaGxhdCIsICJobGFtaW5lciIpKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBtYXAoZ2Vub3R5cGVyLCBmdW5jdGlvbih4KSBzdXBwcmVzc01lc3NhZ2VzKHJlYWRfdHN2KCIuLi8uLi9jb3ZpZC9pc2Ivc2NITEFjb3VudC9CTF9mYXN0cV9maWxlcy50eHQiLCBjb2xfbmFtZXMgPSAic2FtcGxlIikpKSkgJT4lICAtLT4KPCEtLSAgIHVubmVzdChkYXRhKSAlPiUgIC0tPgo8IS0tICAgZmlsdGVyKGdyZXBsKHNhbXAsIHNhbXBsZSkpICU+JSAgLS0+CjwhLS0gICAjIEltcG9ydCBkYXRhIGJhc2VkIG9uIHNhbXBsZSBhbmQgZ2Vub3R5cGVyIC0tPgo8IS0tICAgbXV0YXRlKHJlc3VsdF9wYXRoID0gc3ByaW50ZigiJXMvb3V0cHV0LyVzIixzY0hMQWNvdW50X2RpciwgZ2Vub3R5cGVyKSwgLS0+CjwhLS0gICAgICAgICAgYmFyY29kZV9wYXRoID0gc3ByaW50ZigiJXMvYmFyY29kZXMiLCBzY0hMQWNvdW50X2RpcikpICU+JSAgLS0+CjwhLS0gICAjIGhlYWQoMikgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBwbWFwKGxpc3Qoc2FtcGxlLCByZXN1bHRfcGF0aCwgYmFyY29kZV9wYXRoKSwgZnVuY3Rpb24ocyxyLGIpeyAtLT4KPCEtLSAgICAgc2NITEFfZGF0YV9wcm9jZXNzaW5nKCAtLT4KPCEtLSAgICAgICBzYW1wbGU9cywgLS0+CjwhLS0gICAgICAgcmVzdWx0X2Rpcj1yLCAtLT4KPCEtLSAgICAgICBiYXJjb2RlX2Rpcj1iIC0tPgo8IS0tICAgICApIC0tPgo8IS0tICAgfSkpICU+JSB1bm5lc3QoZGF0YSkgLS0+CjwhLS0geCAtLT4KPCEtLSB5IDwtIHggJT4lICAtLT4KPCEtLSAgIGZpbHRlcihjZWxsICVpbiUgY2VsbF9saXN0KSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKHNhbXBsZSA9IGdzdWIoIl9bQS1aXVswLTldJCIsIiIsc2FtcGxlKSkgJT4lICAtLT4KPCEtLSAgICMgIyBmaWx0ZXIobl9hbGxlbGVzX29ic2VydmVkID09IDIsIGdyZXBsKCJeRFtQUVJdIiwgZ2VuZSksIHNhbXBsZUlEICVpbiUgYygiSU5DT1YwMDMtQUMiLCAiSU5DT1YwMjQtQUMiKSkgJT4lICAtLT4KPCEtLSAgICMgc2VsZWN0KGNlbGwsIGxvY3VzLCBjb3VudCwgZ2VuZV9zdW1fdHlwZWQsIGNlbGx0eXBlKSAlPiUgIC0tPgo8IS0tICAgIyBncm91cF9ieShzZXZlcml0eSwgY2VsbHR5cGUsIGdlbmUpICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZ2VuZSA9PSAiQSIpICU+JSAgLS0+CjwhLS0gICBncm91cF9ieShzYW1wbGUsIGdlbm90eXBlcikgJT4lICAtLT4KPCEtLSAgIHNhbXBsZV9uKDUwMCwgcmVwbGFjZSA9IFQpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShjb21wbGVtZW50ID0gZ2VuZV9zdW1fdHlwZWQgLSBjb3VudCwgZXhwZWN0ZWQgPSAwLjUqZ2VuZV9zdW1fdHlwZWQpICU+JSAgLS0+CjwhLS0gICByb3d3aXNlKCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShvYnNlcnZlZCA9IG1heChhY3Jvc3MoYyhjb3VudCwgY29tcGxlbWVudCkpKSkgJT4lICAtLT4KPCEtLSAgIGdyb3VwX2J5KHNhbXBsZSwgZ2Vub3R5cGVyKSAlPiUgIC0tPgo8IS0tICAgbmVzdCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZGF0YSA9IG1hcChkYXRhLCBzY19tZXRhKSkgJT4lICAtLT4KPCEtLSAgIHVubmVzdChkYXRhKSAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyLCBmaWcud2lkdGggPSAyNCwgZmlnLmhlaWdodCA9IDR9IC0tPgo8IS0tIHkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGdlbm90eXBlciA9IGZhY3RvcihnZW5vdHlwZXIsIGxldmVscyA9IGMoImhsYW1pbmVyIiwicGhsYXQiLCJvcHRpdHlwZSIsImFyY2FzSExBIiwiaW52aXRybyIpKSkgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeCA9IGdlbm90eXBlciwgeSA9IFRFLCB5bWluID0gbG93ZXIsIHltYXggPSB1cHBlcikpKyAtLT4KPCEtLSAgIGdlb21fcG9pbnQoKSsgLS0+CjwhLS0gICBnZW9tX2Vycm9yYmFyKCkrIC0tPgo8IS0tICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikrIC0tPgo8IS0tICAgZmFjZXRfZ3JpZCgufnNhbXBsZSwgc2NhbGVzID0gImZyZWUiKSsgLS0+CjwhLS0gICB0aGVtZV9idygpKyAtLT4KPCEtLSAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSArIC0tPgo8IS0tICAgY29vcmRfZmxpcCgpICsgLS0+CjwhLS0gICBsYWJzKHkgPSAiT2RkcyByYXRpbyBvZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMKSAtLT4KPCEtLSBgYGAgLS0+CgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gc2FtcCA8LSAiSU5DT1YwNjktQkwiIC0tPgo8IS0tIGNlbGxfbGlzdCA8LSBzcnQgJT4lIGZpbHRlcihjZWxsdHlwZSA9PSAiY0RDIikgJT4lIHB1bGwoY2VsbCkgLS0+CjwhLS0geCA8LSB0aWJibGUoZ2Vub3R5cGVyID0gYygiaW52aXRybyIsICJhcmNhc0hMQSIsICJvcHRpdHlwZSIsICJwaGxhdCIsICJobGFtaW5lciIpKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBtYXAoZ2Vub3R5cGVyLCBmdW5jdGlvbih4KSBzdXBwcmVzc01lc3NhZ2VzKHJlYWRfdHN2KCIuLi8uLi9jb3ZpZC9pc2Ivc2NITEFjb3VudC9CTF9mYXN0cV9maWxlcy50eHQiLCBjb2xfbmFtZXMgPSAic2FtcGxlIikpKSkgJT4lICAtLT4KPCEtLSAgIHVubmVzdChkYXRhKSAlPiUgIC0tPgo8IS0tICAgZmlsdGVyKGdyZXBsKHNhbXAsIHNhbXBsZSkpICU+JSAgLS0+CjwhLS0gICAjIEltcG9ydCBkYXRhIGJhc2VkIG9uIHNhbXBsZSBhbmQgZ2Vub3R5cGVyIC0tPgo8IS0tICAgbXV0YXRlKHJlc3VsdF9wYXRoID0gc3ByaW50ZigiJXMvb3V0cHV0LyVzIixzY0hMQWNvdW50X2RpciwgZ2Vub3R5cGVyKSwgLS0+CjwhLS0gICAgICAgICAgYmFyY29kZV9wYXRoID0gc3ByaW50ZigiJXMvYmFyY29kZXMiLCBzY0hMQWNvdW50X2RpcikpICU+JSAgLS0+CjwhLS0gICAjIGhlYWQoMikgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBwbWFwKGxpc3Qoc2FtcGxlLCByZXN1bHRfcGF0aCwgYmFyY29kZV9wYXRoKSwgZnVuY3Rpb24ocyxyLGIpeyAtLT4KPCEtLSAgICAgc2NITEFfZGF0YV9wcm9jZXNzaW5nKCAtLT4KPCEtLSAgICAgICBzYW1wbGU9cywgLS0+CjwhLS0gICAgICAgcmVzdWx0X2Rpcj1yLCAtLT4KPCEtLSAgICAgICBiYXJjb2RlX2Rpcj1iIC0tPgo8IS0tICAgICApIC0tPgo8IS0tICAgfSkpICU+JSB1bm5lc3QoZGF0YSkgLS0+CjwhLS0geSA8LSB4ICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoY2VsbCAlaW4lIGNlbGxfbGlzdCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShzYW1wbGUgPSBnc3ViKCJfW0EtWl1bMC05XSQiLCIiLHNhbXBsZSkpICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZ2VuZSA9PSAiQSIpICU+JSAgLS0+CjwhLS0gICBncm91cF9ieShzYW1wbGUsIGdlbm90eXBlcikgJT4lICAtLT4KPCEtLSAgIHNhbXBsZV9uKDUwMCwgcmVwbGFjZSA9IFQpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShjb21wbGVtZW50ID0gZ2VuZV9zdW1fdHlwZWQgLSBjb3VudCwgZXhwZWN0ZWQgPSAwLjUqZ2VuZV9zdW1fdHlwZWQpICU+JSAgLS0+CjwhLS0gICByb3d3aXNlKCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShvYnNlcnZlZCA9IG1heChhY3Jvc3MoYyhjb3VudCwgY29tcGxlbWVudCkpKSkgJT4lICAtLT4KPCEtLSAgIGdyb3VwX2J5KHNhbXBsZSwgZ2Vub3R5cGVyKSAlPiUgIC0tPgo8IS0tICAgbmVzdCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZGF0YSA9IG1hcChkYXRhLCBzY19tZXRhKSkgJT4lICAtLT4KPCEtLSAgIHVubmVzdChkYXRhKSAtLT4KPCEtLSBwbHRfbWV0YSA8LSB5ICU+JSAgLS0+CjwhLS0gICB1bmdyb3VwKCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHggPSBnZW5vdHlwZXIsIHkgPSBURSwgeW1pbiA9IGxvd2VyLCB5bWF4ID0gdXBwZXIpKSsgLS0+CjwhLS0gICBnZW9tX3BvaW50KCkrIC0tPgo8IS0tICAgZ2VvbV9lcnJvcmJhcigpKyAtLT4KPCEtLSAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpKyAtLT4KPCEtLSAgIHRoZW1lX2J3KCkrIC0tPgo8IS0tICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAtLT4KPCEtLSAgIGxhYnMoeSA9ICJPZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwpIC0tPgo8IS0tIHBsdF9tZXRhICAgLS0+CjwhLS0gYGBgIC0tPgo8IS0tICMjIyBBbGwgY2x1c3RlcnMgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBzYW1wIDwtICJJTkNPVjA2OS1CTCIgLS0+CjwhLS0gY2VsbF9saXN0IDwtIHNydCAlPiUgZmlsdGVyKGNlbGx0eXBlID09ICJjREMiKSAlPiUgcHVsbChjZWxsKSAtLT4KPCEtLSB4IDwtIHRpYmJsZShnZW5vdHlwZXIgPSBjKCJpbnZpdHJvIiwgImFyY2FzSExBIiwgIm9wdGl0eXBlIiwgInBobGF0IiwgImhsYW1pbmVyIikpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZGF0YSA9IG1hcChnZW5vdHlwZXIsIGZ1bmN0aW9uKHgpIHN1cHByZXNzTWVzc2FnZXMocmVhZF90c3YoIi4uLy4uL2NvdmlkL2lzYi9zY0hMQWNvdW50L0JMX2Zhc3RxX2ZpbGVzLnR4dCIsIGNvbF9uYW1lcyA9ICJzYW1wbGUiKSkpKSAlPiUgIC0tPgo8IS0tICAgdW5uZXN0KGRhdGEpICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZ3JlcGwoc2FtcCwgc2FtcGxlKSkgJT4lICAtLT4KPCEtLSAgICMgSW1wb3J0IGRhdGEgYmFzZWQgb24gc2FtcGxlIGFuZCBnZW5vdHlwZXIgLS0+CjwhLS0gICBtdXRhdGUocmVzdWx0X3BhdGggPSBzcHJpbnRmKCIlcy9vdXRwdXQvJXMiLHNjSExBY291bnRfZGlyLCBnZW5vdHlwZXIpLCAtLT4KPCEtLSAgICAgICAgICBiYXJjb2RlX3BhdGggPSBzcHJpbnRmKCIlcy9iYXJjb2RlcyIsIHNjSExBY291bnRfZGlyKSkgJT4lICAtLT4KPCEtLSAgICMgaGVhZCgyKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUoZGF0YSA9IHBtYXAobGlzdChzYW1wbGUsIHJlc3VsdF9wYXRoLCBiYXJjb2RlX3BhdGgpLCBmdW5jdGlvbihzLHIsYil7IC0tPgo8IS0tICAgICBzY0hMQV9kYXRhX3Byb2Nlc3NpbmcoIC0tPgo8IS0tICAgICAgIHNhbXBsZT1zLCAtLT4KPCEtLSAgICAgICByZXN1bHRfZGlyPXIsIC0tPgo8IS0tICAgICAgIGJhcmNvZGVfZGlyPWIgLS0+CjwhLS0gICAgICkgLS0+CjwhLS0gICB9KSkgJT4lIHVubmVzdChkYXRhKSAtLT4KPCEtLSByZXNhbXBsZV9kZXB0aCA8LSA1MDAgLS0+CjwhLS0geSA8LSB4ICU+JSAgLS0+CjwhLS0gICBsZWZ0X2pvaW4oc3J0ICU+JSBzZWxlY3QoY2VsbHR5cGUsIGNlbGwpLCBieSA9ICJjZWxsIikgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShzYW1wbGUgPSBnc3ViKCJfW0EtWl1bMC05XSQiLCIiLHNhbXBsZSkpICU+JSAgLS0+CjwhLS0gICAjIGZpbHRlcihnZW5lID09ICJBIikgJT4lICAtLT4KPCEtLSAgIGZpbHRlcighaXMubmEoY2VsbHR5cGUpKSAlPiUgIC0tPgo8IS0tICAgdW5pdGUoImdyb3VwIiwgY2VsbHR5cGUsIGdlbmUsIHNlcCA9ICJfIiwgcmVtb3ZlID0gRikgJT4lICAtLT4KPCEtLSAgICMgbXV0YXRlKGdyb3VwID0gY2VsbHR5cGUpICU+JSAgLS0+CjwhLS0gICBncm91cF9ieShncm91cCkgJT4lIG5lc3QoKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBtYXAoZGF0YSwgZnVuY3Rpb24oeCl7IC0tPgo8IS0tICAgICB4ICU+JSAgLS0+CjwhLS0gICAgICAgZ3JvdXBfYnkoc2FtcGxlLCBnZW5vdHlwZXIpICU+JSAgLS0+CjwhLS0gICAgICAgIyBzYW1wbGVfbihyZXNhbXBsZV9kZXB0aCwgcmVwbGFjZSA9IFQpICU+JSAgLS0+CjwhLS0gICAgICAgbXV0YXRlKGNvbXBsZW1lbnQgPSBnZW5lX3N1bV90eXBlZCAtIGNvdW50LCBleHBlY3RlZCA9IDAuNSpnZW5lX3N1bV90eXBlZCkgJT4lICAtLT4KPCEtLSAgICAgICByb3d3aXNlKCkgJT4lICAtLT4KPCEtLSAgICAgICBtdXRhdGUob2JzZXJ2ZWQgPSBtYXgoYWNyb3NzKGMoY291bnQsIGNvbXBsZW1lbnQpKSkpICU+JSAgLS0+CjwhLS0gICAgICAgZ3JvdXBfYnkoc2FtcGxlLCBnZW5vdHlwZXIpICU+JSAgLS0+CjwhLS0gICAgICAgbmVzdCgpICU+JSAgLS0+CjwhLS0gICAgICAgbXV0YXRlKGRhdGEgPSBtYXAoZGF0YSwgc2NfbWV0YSkpICU+JSAgLS0+CjwhLS0gICAgICAgdW5uZXN0KGRhdGEpIC0tPgo8IS0tICAgfSkpICU+JSAgLS0+CjwhLS0gICB1bm5lc3QoZGF0YSkgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0ID0gNH0gLS0+CjwhLS0geSA8LSB5ICU+JSAgLS0+CjwhLS0gICBzZXBhcmF0ZShncm91cCwgaW50byA9IGMoImNlbGx0eXBlIiwgImdlbmUiKSwgc2VwID0gIl8iKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGdyb3VwID0gZmFjdG9yKGNlbGx0eXBlLCBsZXZlbHMgPSBjKCAtLT4KPCEtLSAgICAgImNEQyIsICJDRDE0IE1vbm9jeXRlIiwgIkIiLCAicERDIiwgIkNEMTYgTW9ub2N5dGUiLCAiQ0Q4IFQiLCAiQ0Q0IFQiLCAiTksiIC0tPgo8IS0tICAgKSkpIC0tPgo8IS0tIHkgJT4lICAtLT4KPCEtLSAgIHVuZ3JvdXAoKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeCA9IGdlbm90eXBlciwgeSA9IFRFLCB5bWluID0gbG93ZXIsIHltYXggPSB1cHBlciwgZmlsbCA9IGdyb3VwKSkrIC0tPgo8IS0tICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4yNSkrIC0tPgo8IS0tICAgZ2VvbV9lcnJvcmJhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoID0wLjUpKyAtLT4KPCEtLSAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpKyAtLT4KPCEtLSAgIHRoZW1lX2J3KCkrIC0tPgo8IS0tICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAtLT4KPCEtLSAgIGxhYnMoeSA9ICJPZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwpICsgLS0+CjwhLS0gICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgLS0+Cgo8IS0tIHkgJT4lICAtLT4KPCEtLSAgIHVuZ3JvdXAoKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeCA9IGdlbm90eXBlciwgeSA9IFRFLCB5bWluID0gbG93ZXIsIHltYXggPSB1cHBlciwgZmlsbCA9IGdyb3VwKSkrIC0tPgo8IS0tICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4yNSkrIC0tPgo8IS0tICAgZ2VvbV9lcnJvcmJhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoID0wLjUpKyAtLT4KPCEtLSAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpKyAtLT4KPCEtLSAgIHRoZW1lX2J3KCkrIC0tPgo8IS0tICAgZmFjZXRfd3JhcCh+Z3JvdXAsIG5jb2wgPSA0KSsgLS0+CjwhLS0gICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIC0tPgo8IS0tICAgbGFicyh5ID0gIk9kZHMgcmF0aW8gb2YgZG9taW5hbnQgYWxsZWxlIiwgeD0gTlVMTCkgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSAtLT4KCjwhLS0geSAlPiUgIC0tPgo8IS0tICAgdW5ncm91cCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgIC0tPgo8IS0tICAgZ2dwbG90KGFlcyh4ID0gZ3JvdXAsIHkgPSBURSwgeW1pbiA9IGxvd2VyLCB5bWF4ID0gdXBwZXIsIGZpbGwgPSBjZWxsdHlwZSkpKyAtLT4KPCEtLSAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMjUpKyAtLT4KPCEtLSAgIGdlb21fZXJyb3JiYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjkpLCB3aWR0aCA9MC41KSsgLS0+CjwhLS0gICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsgLS0+CjwhLS0gICB0aGVtZV9idygpKyAtLT4KPCEtLSAgIGZhY2V0X2dyaWQoZ2VuZX5nZW5vdHlwZXIsIHNjYWxlcyA9ICJmcmVlX3kiKSsgLS0+CjwhLS0gICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIC0tPgo8IS0tICAgbGFicyh5ID0gIk9kZHMgcmF0aW8gb2YgZG9taW5hbnQgYWxsZWxlIiwgeD0gTlVMTCkgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSAtLT4KCjwhLS0geSAlPiUgIC0tPgo8IS0tICAgdW5ncm91cCgpICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZ3JvdXAgPT0gInBEQyIpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgIC0tPgo8IS0tICAgZ2dwbG90KGFlcyh4ID0gZ2Vub3R5cGVyLCB5ID0gVEUsIHltaW4gPSBsb3dlciwgeW1heCA9IHVwcGVyKSkrIC0tPgo8IS0tICAgZ2VvbV9wb2ludCgpKyAtLT4KPCEtLSAgIGdlb21fZXJyb3JiYXIoKSsgLS0+CjwhLS0gICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsgLS0+CjwhLS0gICB0aGVtZV9idygpKyAtLT4KPCEtLSAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgLS0+CjwhLS0gICBsYWJzKHkgPSAiT2RkcyByYXRpbyBvZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMKSAtLT4KPCEtLSBgYGAgLS0+CgoKCgo8IS0tICMgQXNzZW1ibGUgcGxvdCAtLT4KPCEtLSBgYGB7ciwgZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQgPSA2fSAtLT4KPCEtLSAjIHBsdF9hbGxlbGVfZnJlcV9sZ2QgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfYWxsZWxlX2ZyZXEpIC0tPgo8IS0tICMgcGx0X21heF9hbGxlbGVfbGdkIDwtIGNvd3Bsb3Q6OmdldF9sZWdlbmQocGx0X21heF9hbGxlbGUpIC0tPgo8IS0tIGNvbF8xIDwtIHBsb3RfZ3JpZCggLS0+CjwhLS0gICBwbHRfYWxsZWxlX2ZyZXEsIC0tPgo8IS0tICAgcGx0X21heF9hbGxlbGUsIC0tPgo8IS0tICAgbmNvbCA9IDEsIC0tPgo8IS0tICAgcmVsX2hlaWdodHMgPSBjKDUsMyksIC0tPgo8IS0tICAgYWxpZ24gPSAidiIsIGF4aXMgPSAibHIiLCAtLT4KPCEtLSAgIGxhYmVscyA9IExFVFRFUlNbMToyXSAtLT4KPCEtLSApIC0tPgoKPCEtLSBjb2xfMiA8LSBwbG90X2dyaWQoIC0tPgo8IS0tICAgcGx0X3NydCwgLS0+CjwhLS0gICBwbHRfbWV0YSwgLS0+CjwhLS0gICBuY29sID0gMSwgLS0+CjwhLS0gICBhbGlnbiA9ICJ2IiwgYXhpcyA9ICJsciIsIC0tPgo8IS0tICAgbGFiZWxzID0gTEVUVEVSU1szOjRdLCAtLT4KPCEtLSAgIGhqdXN0ID0gMC41IC0tPgo8IS0tICkgLS0+CjwhLS0gcGxvdF9ncmlkKCAtLT4KPCEtLSAgIGNvbF8xLCAtLT4KPCEtLSAgIGNvbF8yLCAgLS0+CjwhLS0gICBucm93ID0gMSwgLS0+CjwhLS0gICByZWxfd2lkdGhzID0gYyg2LDMpIC0tPgo8IS0tICkgLS0+CjwhLS0gYGBgIC0tPgoKCgoKCgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gZyA8LSBnZ3Bsb3RfYnVpbGQocGx0X3NydCkgLS0+Cgo8IS0tIHBsdF9pZHMgPC0gZyRkYXRhW1sxXV0gLS0+CjwhLS0gZ3JvdXBfbGV2ZWxzIDwtIGxldmVscyhmYWN0b3IoZyRwbG90JGRhdGFbW2ckcGxvdCRsYWJlbHMkY29sb3VyXV0pKSAtLT4KCjwhLS0gcGx0X2tleSA8LSBnJGRhdGFbWzFdXSAlPiUgIC0tPgo8IS0tICAgc2VsZWN0KGNvbG91ciwgZ3JvdXApICU+JSAgLS0+CjwhLS0gICBkaXN0aW5jdCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUobGFiZWwgPSBtYXBfY2hyKGdyb3VwLCBmdW5jdGlvbih4KSBncm91cF9sZXZlbHNbeF0pKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGxhYmVsID0gZmFjdG9yKGxhYmVsLCBsZXZlbCA9IGdyb3VwX2xldmVscykpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShsYWJlbCA9IHNwcmludGYoIiVzKSAlcyIsIDE6bigpLCBsYWJlbCkpICU+JSAgLS0+CjwhLS0gICBzZWxlY3QoLWdyb3VwKSAtLT4KCjwhLS0gICAjIGxldmVscyhmYWN0b3IoZyRwbG90JGRhdGFbW2ckcGxvdCRsYWJlbHMkY29sb3VyXV0pKSAtLT4KPCEtLSAgICMgZGF0YS5mcmFtZShjb2xvdXJzID0gdW5pcXVlKGckZGF0YVtbMV1dWyJjb2xvdXIiXSksICAtLT4KPCEtLSAgICMgICAgICAgICAgICAgIGxhYmVsID0gbGV2ZWxzKGZhY3RvcihnJHBsb3QkZGF0YVtbZyRwbG90JGxhYmVscyRjb2xvdXJdXSkpKSAlPiUgIC0tPgoKPCEtLSBwbHRfZGYgPC0gcGx0X2lkcyAlPiUgIC0tPgo8IS0tICAgbGVmdF9qb2luKHBsdF9rZXksIGJ5ID0gImNvbG91ciIpIC0tPgoKPCEtLSBwbHRfY2VudGVyIDwtIHBsdF9kZiAlPiUgIC0tPgo8IS0tICAgZ3JvdXBfYnkobGFiZWwpICU+JSBzdW1tYXJpc2UoeCA9IG1lYW4oeCksIHkgPSBtZWFuKHkpKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUobGFiZWwgPSBnc3ViKCIpLioiLCIiLGxhYmVsKSkgLS0+Cgo8IS0tIHBsdF9yZXBlbCA8LSBwbHRfZGYgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeD14LHk9eSxjb2xvcj1sYWJlbCkpICsgLS0+CjwhLS0gICBnZW9tX3BvaW50KHNpemUgPSAwLjUpKyAtLT4KPCEtLSAgICMgZ2VvbV9wb2ludChkYXRhID0gcGx0X2NlbnRlciwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgLS0+CjwhLS0gICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YT1wbHRfY2VudGVyLGFlcyhsYWJlbD1sYWJlbCwgIGJnLmNvbG9yPSJ3aGl0ZSIsIGJnLnI9MC4yNSxtaW4uc2VnbWVudC5sZW5ndGggPSAwKSxjb2xvciA9ICJibGFjayIsZm9udGZhY2UgPSAiYm9sZCIpICsgLS0+CjwhLS0gICB0aGVtZV9idygpICsgLS0+CjwhLS0gICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMikgKSApICsgLS0+CjwhLS0gICBsYWJzKHg9IlVNQVAgMSIsIHkgPSAiVU1BUCAyIiwgY29sb3IgPSAiQ2VsbCBDbHVzdGVyIikgKyAtLT4KPCEtLSAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikrICAtLT4KPCEtLSAgIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkgLS0+CjwhLS0gcGx0X3JlcGVsIC0tPgoKPCEtLSBgYGAgLS0+CgoKCgoKCg==